舒一笑的网站:www.shuyixiao.cloud 里面:面试八股文、BAT面试真题、工作内推、工作经验分享、技术专栏等等什么都有,欢迎收藏和转发。
优化数据呈现:打造领导友好的数据库汇报方案
在本次分享中,我将探讨一个完整的数据处理与可视化流程,旨在将复杂的系统日志信息转化为直观、易懂的图形展示,以便非技术背景的领导能够轻松理解数据背后的故事。整个过程包括使用DataGrip高效查询日志数据、Java进行数据二次加工以及利用jfreechart库实现数据可视化。此外,我们还将简要介绍如何利用easyexcel和lombok等Java库来辅助数据处理和代码简化,共同构建一个高效、友好的数据展示解决方案。
本次日志涉及库表信息展示
操作日志表结构展示
-- auto-generated definition
create table log_operate_log
(
id bigint auto_increment comment '日志主键'
primary key,
trace_id varchar(64) charset utf8mb4 null,
user_id bigint null comment '用户编号',
user_type tinyint default 0 not null comment '用户类型',
module varchar(50) charset utf8mb4 null,
name varchar(50) charset utf8mb4 null,
type bigint default 0 not null comment '操作分类',
content varchar(2000) charset utf8mb4 null,
exts varchar(512) charset utf8mb4 null,
request_method varchar(16) charset utf8mb4 null,
request_url varchar(255) charset utf8mb4 null,
user_ip varchar(50) charset utf8mb4 null,
user_agent varchar(200) charset utf8mb4 null,
java_method varchar(512) charset utf8mb4 null,
java_method_args varchar(7936) charset utf8mb4 null,
start_time datetime not null comment '操作时间',
duration int not null comment '执行时长',
result_code int default 0 not null comment '结果码',
result_msg varchar(512) charset utf8mb4 null,
result_data varchar(4000) charset utf8mb4 null,
creator varchar(64) charset utf8mb4 null,
creator_dept varchar(64) charset utf8mb4 null,
create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updater varchar(64) charset utf8mb4 null,
update_time datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
deleted bit default b'0' not null comment '是否删除',
tenant_id bigint default 0 not null comment '租户编号'
)
comment '操作日志记录' collate = utf8mb4_unicode_ci;
菜单权限表结构展示
-- auto-generated definition
create table sys_menu
(
id bigint auto_increment comment '菜单ID'
primary key,
parent_id bigint null comment '父id',
name varchar(255) charset utf8 null comment '菜单标题',
url varchar(255) charset utf8 null comment '路径',
component varchar(255) charset utf8 null comment '组件',
menu_role_type tinyint(1) null comment '菜单角色类别(1:管理角色,2:业务角色)',
iz_route tinyint(1) default 1 null comment '是否路由菜单: 0:不是 1:是(默认值1)',
component_name varchar(255) charset utf8 null comment '组件名字',
redirect varchar(255) charset utf8 null comment '一级菜单跳转地址',
menu_type int null comment '菜单类型(0:一级菜单; 1:子菜单:2:按钮权限)',
permission varchar(255) charset utf8 null comment '菜单权限编码',
permission_type varchar(10) charset utf8 default '0' null comment '权限策略1显示2禁用',
sort double(8, 2) not null comment '菜单排序',
always_show tinyint(1) null comment '聚合子路由: 1是0否',
icon varchar(255) charset utf8 null comment '菜单图标',
iz_leaf tinyint(1) null comment '是否叶子节点: 1是0否',
keep_alive tinyint(1) null comment '是否缓存该页面: 1:是 0:不是',
hidden int(2) default 0 null comment '是否隐藏路由: 0否,1是',
hide_tab int(2) default 0 null comment '是否隐藏tab: 0否,1是',
description varchar(255) charset utf8 null comment '描述',
rule_flag int(3) default 0 null comment '是否添加数据权限1是0否',
status int(2) null comment '按钮权限状态(1无效0有效)',
internal_or_external tinyint(1) null comment '外链菜单打开方式 0/内部打开 1/外部打开',
creator varchar(64) charset utf8mb4 null,
creator_dept varchar(64) charset utf8mb4 null,
create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updater varchar(64) charset utf8mb4 null,
update_time datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
deleted bit default b'0' not null comment '是否删除'
)
comment '菜单权限表' collate = utf8mb4_unicode_ci;
用户表结构展示
-- auto-generated definition
create table sys_users
(
id bigint auto_increment comment '用户ID'
primary key,
usercode varchar(64) null,
username varchar(30) null,
password varchar(100) null,
nickname varchar(30) null,
remark varchar(2000) null,
dept_id bigint null comment '部门ID',
post_ids varchar(255) null,
user_type tinyint default 1 null comment '用户类型 1业务 2管理',
email varchar(50) null,
mobile varchar(11) null,
sex tinyint default 0 null comment '用户性别',
avatar varchar(2000) null,
status tinyint default 0 not null comment '帐号状态(0正常 1停用)',
login_ip varchar(50) null,
login_date datetime default CURRENT_TIMESTAMP null comment '最后登录时间',
password_date datetime null comment '最后修改密码时间',
lock_date datetime null comment '最后锁定时间',
valid_date datetime null comment '账号有效时间',
creator varchar(64) null,
creator_dept varchar(64) default '' null comment '创建者部门',
create_time datetime default CURRENT_TIMESTAMP null comment '创建时间',
updater varchar(64) null,
update_time datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间',
deleted bit default b'0' null comment '是否删除',
tenant_id bigint default 0 null comment '租户编号',
constraint index_id
unique (id)
);
本次展示需要实现的效果
如何需要根据系统的日志信息得出不同人员访问不同菜单的频率、以及对应的次数情况。当然这些信息能使用到sql直接查询得出。但是本地介绍如何使用Java代码的方式进行对得到的数据进行二次加工,并且使用jfreechart三方库进行对可视化图表的渲染与展示,下面进行一下基础查询的SQL展示。
统计每个菜单访问次数情况SQL示例
SELECT
m.name AS menu_name, -- 菜单名称
COUNT(l.start_time) AS access_count -- 统计每个菜单的访问次数
FROM
log_operate_log l
JOIN
sys_menu m ON l.request_url LIKE CONCAT(m.url, '%') -- 关联菜单表
JOIN
sys_users u ON l.user_id = u.id -- 关联用户表
GROUP BY
m.name -- 按菜单名称分组
ORDER BY
access_count DESC; -- 按访问次数降序排序
这里给大家介绍一个技巧就是可以使用DataGrip中的导出为excel然后使用Java代码方式,结合EasyExcel进行对数据的处理
只需要在这里进行进行选择位置之后,导出相对应的excel数据。
如何使用Java代码的方式进行读取数据&&使用jfreechart进行可视化渲染。
这里需要注意一下就是需要在POM文件中配置jfreechart等三方的依赖然后使用下面的代码逻辑就可以实现对图片的渲染
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>4.0.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.jfree</groupId>
<artifactId>jfreechart</artifactId>
<version>1.5.5</version>
</dependency>
</dependencies>
折线图图片展示
/**
* Copyright © 2024年 integration-projects-maven. All rights reserved.
* ClassName QuickExcelReading.java
* author 舒一笑 [email protected]
* version 1.0.0
* Description 快速Excel读取次数与频率
* createTime 2024年09月18日 11:11:00
*/
@Slf4j
public class QuickExcelReading {
public static void main(String[] args) {
new QuickExcelReading().simpleRead();
}
public void simpleRead() {
String fileName = "excel-quick-java/src/main/resources/菜单访问总数汇总.xlsx";
// 使用PageReadListener读取数据,修改批处理数量为200
EasyExcel.read(fileName, DemoData.class, new PageReadListener<DemoData>(dataList -> {
// 使用Map<String, Integer> 来统计每个menu_name的访问次数
Map<String, Integer> menuAccessCountMap = new HashMap<>();
int totalAccessCount = 0;
// 遍历每条数据
for (DemoData demoData : dataList) {
log.info("读取到一条数据: {}", demoData);
// 累计各menu_name的访问次数
menuAccessCountMap.merge(demoData.getMenuName(), demoData.getAccessCount(), Integer::sum);
totalAccessCount += demoData.getAccessCount();
}
// 对map按照访问次数进行倒序排序,并只取前10项
Map<String, Integer> sortedMenuAccessCountMap = menuAccessCountMap.entrySet()
.stream()
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue())) // 按访问次数降序排序
.limit(10) // 只取前10个
.collect(java.util.stream.Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldValue, newValue) -> oldValue,
java.util.LinkedHashMap::new
));
// 开始构建Markdown表格
StringBuilder markdownTable = new StringBuilder();
markdownTable.append("| 功能名称 | 访问次数 | 占比 |\n");
markdownTable.append("| --- | --- | --- |\n");
// 计算每个menu_name的访问次数占比并生成Markdown表格行
int finalTotalAccessCount = totalAccessCount;
sortedMenuAccessCountMap.forEach((menuName, count) -> {
double percentage = (count * 100.0) / finalTotalAccessCount;
markdownTable.append(String.format("| %s | %d次 | %.2f%% |\n", menuName, count, percentage));
});
// 输出Markdown表格
System.out.println(markdownTable.toString());
// 渲染饼图
renderPieChart(sortedMenuAccessCountMap);
}, 200)) // 批处理大小设为200
.sheet().doRead();
}
// 渲染饼图的方法
public void renderPieChart(Map<String, Integer> sortedMenuAccessCountMap) {
DefaultPieDataset dataset = new DefaultPieDataset();
// 将sortedMenuAccessCountMap数据放入dataset中
sortedMenuAccessCountMap.forEach(dataset::setValue);
// 创建饼图
JFreeChart pieChart = ChartFactory.createPieChart(
"功能访问次数分布(前10项)", // 图表标题
dataset, // 数据集
true, // 是否显示图例
true, // 是否使用工具提示
false // 是否生成URLs
);
// 自定义饼图样式
PiePlot plot = (PiePlot) pieChart.getPlot();
plot.setLabelFont(new java.awt.Font("SansSerif", java.awt.Font.PLAIN, 12)); // 设置字体大小
plot.setNoDataMessage("没有数据");
plot.setCircular(true); // 设置为圆形饼图
plot.setLabelGap(0.02); // 设置标签与饼图的距离
// 设置标签在饼图外部显示
plot.setSimpleLabels(false); // 使用连接线将标签显示在饼图外部
// 保存饼图为图片
int width = 960; // 增加图片宽度
int height = 720; // 增加图片高度
File pieChartFile = new File("功能访问次数分布饼图.png");
try {
ChartUtils.saveChartAsPNG(pieChartFile, pieChart, width, height);
System.out.println("饼图已生成: " + pieChartFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
}
折线图图片性质展示
运行代码之后就可以在资源路径下得到图片的信息
渲染的图片图片展示(这里有一个坑需要注意就是这个三方框架对中文好像不是很友好,然后就是出现这下面图片的效果)
柱状图效果展示
代码展示
/**
* Copyright © 2024年 integration-projects-maven. All rights reserved.
* ClassName QuickExcelReading2.java
* author 舒一笑 [email protected]
* version 1.0.0
* Description 快速Excel读取次数与频率人员访问情况
* createTime 2024年09月18日 12:59:00
*/
@Slf4j
public class QuickExcelReading2 {
public static void main(String[] args) {
new QuickExcelReading2().simpleRead();
}
public void simpleRead() {
String fileName = "E:\\ALearn\\code-improves-excel-efficiency\\excel-quick-java\\src\\main\\resources\\用户访问次数.xlsx";
// 使用PageReadListener读取数据,修改批处理数量为200
EasyExcel.read(fileName, DemoData.class, new PageReadListener<DemoData>(dataList -> {
// 使用Map<String, Integer> 来统计每个menu_name的访问次数
Map<String, Integer> menuAccessCountMap = new HashMap<>();
int totalAccessCount = 0;
// 遍历每条数据
for (DemoData demoData : dataList) {
log.info("读取到一条数据: {}", demoData);
if (menuAccessCountMap.get(demoData.getNickName()) == null){
menuAccessCountMap.put(demoData.getNickName(), demoData.getAccessCount());
totalAccessCount += demoData.getTotalAccessCount();
}
// 累计各menu_name的访问次数
// menuAccessCountMap.merge(demoData.getNickName(), demoData.getAccessCount(), Integer::sum);
// totalAccessCount += demoData.getTotalAccessCount();
}
// 对map按照访问次数进行倒序排序,并只取前10项
Map<String, Integer> sortedMenuAccessCountMap = menuAccessCountMap.entrySet()
.stream()
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue())) // 按访问次数降序排序
.limit(10) // 只取前10个
.collect(java.util.stream.Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldValue, newValue) -> oldValue,
java.util.LinkedHashMap::new
));
// 开始构建Markdown表格
StringBuilder markdownTable = new StringBuilder();
markdownTable.append("| 功能名称 | 访问次数 | 占比 |\n");
markdownTable.append("| --- | --- | --- |\n");
// 计算每个menu_name的访问次数占比并生成Markdown表格行
int finalTotalAccessCount = totalAccessCount;
sortedMenuAccessCountMap.forEach((menuName, count) -> {
double percentage = (count * 100.0) / finalTotalAccessCount;
markdownTable.append(String.format("| %s | %d次 | %.2f%% |\n", menuName, count, percentage));
});
// 输出Markdown表格
System.out.println(markdownTable.toString());
// 渲染饼图
renderPieChart(sortedMenuAccessCountMap);
// 渲染柱状图
renderBarChart(sortedMenuAccessCountMap);
}, 65588)) // 批处理大小设为200
.sheet().doRead();
}
// 渲染饼图的方法
public void renderPieChart(Map<String, Integer> sortedMenuAccessCountMap) {
DefaultPieDataset dataset = new DefaultPieDataset();
// 将sortedMenuAccessCountMap数据放入dataset中
sortedMenuAccessCountMap.forEach(dataset::setValue);
// 创建饼图
JFreeChart pieChart = ChartFactory.createPieChart(
"功能访问次数分布(前10项)", // 图表标题
dataset, // 数据集
true, // 是否显示图例
true, // 是否使用工具提示
false // 是否生成URLs
);
// 自定义饼图样式
PiePlot plot = (PiePlot) pieChart.getPlot();
plot.setLabelFont(new java.awt.Font("SansSerif", java.awt.Font.PLAIN, 12)); // 设置字体大小
plot.setNoDataMessage("没有数据");
plot.setCircular(true); // 设置为圆形饼图
plot.setLabelGap(0.02); // 设置标签与饼图的距离
// 设置标签在饼图外部显示
plot.setSimpleLabels(false); // 使用连接线将标签显示在饼图外部
// 保存饼图为图片
int width = 960; // 增加图片宽度
int height = 720; // 增加图片高度
File pieChartFile = new File("人员访问次数分布饼图.png");
try {
ChartUtils.saveChartAsPNG(pieChartFile, pieChart, width, height);
System.out.println("饼图已生成: " + pieChartFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
// 渲染柱状图的方法
public void renderBarChart(Map<String, Integer> sortedMenuAccessCountMap) {
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
// 将sortedMenuAccessCountMap数据放入dataset中
sortedMenuAccessCountMap.forEach((menuName, count) -> {
dataset.addValue(count, "访问次数", menuName);
});
// 创建柱状图
JFreeChart barChart = ChartFactory.createBarChart(
"功能访问次数分布(前10项)", // 图表标题
"功能名称", // X轴标签
"访问次数", // Y轴标签
dataset, // 数据集
PlotOrientation.VERTICAL, // 图表方向:垂直
true, // 是否显示图例
true, // 是否使用工具提示
false // 是否生成URLs
);
// 自定义柱状图样式
CategoryPlot plot = barChart.getCategoryPlot();
plot.setRangeGridlinePaint(java.awt.Color.BLACK); // 设置网格线颜色
BarRenderer renderer = (BarRenderer) plot.getRenderer();
renderer.setDrawBarOutline(false); // 不绘制边框
// 设置柱状图的柱子颜色
renderer.setSeriesPaint(0, new java.awt.Color(79, 129, 189)); // 自定义颜色
// 保存柱状图为图片
int width = 960; // 图片宽度
int height = 720; // 图片高度
File barChartFile = new File("人员访问次数分布柱状图.png");
try {
ChartUtils.saveChartAsPNG(barChartFile, barChart, width, height);
System.out.println("柱状图已生成: " + barChartFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
}
好了,本次的分享就先到这里,下次再给大家分享一下如何使用python结合三方库进行图片的渲染,这方面不得不说确实python要强很多~
标签:comment,Java,后端,default,charset,utf8mb4,可视化,varchar,null From: https://blog.51cto.com/u_16656615/12047580