feat(performance): 新增分组绩效统计功能
- 在性能监控页面添加菜单分类总览模块,显示各模块操作量对比 - 实现菜单分组柱状图可视化,支持点击筛选功能 - 添加分类模块明细折叠面板,展示具体模块统计数据 - 集成后端菜单分组绩效接口,支持按一级菜单路径过滤 - 添加当前分类筛选标签显示,增强用户体验 - 优化图表渲染逻辑,增加空数据情况下的图表清理 - 完善响应式布局,适配不同屏幕尺寸调整
This commit is contained in:
@@ -40,4 +40,9 @@ public class OperPerformanceQuery implements Serializable {
|
||||
* 模块标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* oper_page 路径前缀(用于按一级菜单过滤,如 /wip)
|
||||
*/
|
||||
private String operPagePrefix;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.klp.system.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 菜单分组绩效统计 VO(按一级菜单 oper_page 归类)
|
||||
*
|
||||
* @author Reasonix
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class OperMenuGroupVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 菜单名称(驾驶舱、生产管控……) */
|
||||
private String menuName;
|
||||
|
||||
/** 菜单路由路径前缀(post、wip……) */
|
||||
private String menuPath;
|
||||
|
||||
/** 该分类操作总次数 */
|
||||
private Long totalCount;
|
||||
|
||||
/** 成功次数 */
|
||||
private Long successCount;
|
||||
|
||||
/** 失败次数 */
|
||||
private Long failCount;
|
||||
|
||||
/** 成功率 (%) */
|
||||
private Double successRate;
|
||||
|
||||
/** 使用人数 */
|
||||
private Long personCount;
|
||||
|
||||
/** 该分类下的模块明细列表 */
|
||||
private List<OperModuleStatVO> modules = new ArrayList<>();
|
||||
}
|
||||
@@ -16,6 +16,11 @@ public class OperModuleStatVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 所属菜单分组(菜单名称,如"驾驶舱")
|
||||
*/
|
||||
private String menuGroup;
|
||||
|
||||
/**
|
||||
* 模块标题
|
||||
*/
|
||||
|
||||
@@ -35,4 +35,9 @@ public interface SysOperLogMapper extends BaseMapperPlus<SysOperLogMapper, SysOp
|
||||
* 全局概览统计
|
||||
*/
|
||||
OperSummaryVO selectGlobalSummary(OperPerformanceQuery query);
|
||||
|
||||
/**
|
||||
* 按菜单分组+模块明细统计(oper_page 匹配一级菜单)
|
||||
*/
|
||||
List<OperModuleStatVO> selectMenuGroupDetail(OperPerformanceQuery query);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.system.domain.SysOperLog;
|
||||
import com.klp.system.domain.bo.OperPerformanceQuery;
|
||||
import com.klp.system.domain.vo.OperMenuGroupVO;
|
||||
import com.klp.system.domain.vo.OperModuleStatVO;
|
||||
import com.klp.system.domain.vo.OperPersonVO;
|
||||
import com.klp.system.domain.vo.OperSummaryVO;
|
||||
@@ -69,4 +70,9 @@ public interface ISysOperLogService {
|
||||
* 模块使用排行
|
||||
*/
|
||||
List<OperModuleStatVO> selectModuleRanking(OperPerformanceQuery query);
|
||||
|
||||
/**
|
||||
* 按菜单分组绩效统计(oper_page 匹配一级菜单 path)
|
||||
*/
|
||||
List<OperMenuGroupVO> selectMenuGroupPerformance(OperPerformanceQuery query);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.klp.common.utils.StringUtils;
|
||||
import com.klp.common.utils.ip.AddressUtils;
|
||||
import com.klp.system.domain.SysOperLog;
|
||||
import com.klp.system.domain.bo.OperPerformanceQuery;
|
||||
import com.klp.system.domain.vo.OperMenuGroupVO;
|
||||
import com.klp.system.domain.vo.OperModuleStatVO;
|
||||
import com.klp.system.domain.vo.OperPersonVO;
|
||||
import com.klp.system.domain.vo.OperSummaryVO;
|
||||
@@ -151,6 +152,86 @@ public class SysOperLogServiceImpl implements ISysOperLogService {
|
||||
return baseMapper.selectModuleSummary(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OperMenuGroupVO> selectMenuGroupPerformance(OperPerformanceQuery query) {
|
||||
// 1. 查询原始数据(按 menu_group + title 双维度聚合)
|
||||
List<OperModuleStatVO> rawList = baseMapper.selectMenuGroupDetail(query);
|
||||
if (rawList == null || rawList.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 2. 按 menuGroup 分组
|
||||
Map<String, List<OperModuleStatVO>> groupMap = rawList.stream()
|
||||
.collect(Collectors.groupingBy(
|
||||
m -> m.getMenuGroup() != null ? m.getMenuGroup() : "其他",
|
||||
LinkedHashMap::new,
|
||||
Collectors.toList()
|
||||
));
|
||||
|
||||
// 3. 固定顺序
|
||||
List<String> orderNames = java.util.Arrays.asList(
|
||||
"驾驶舱", "生产管控", "数字仓储", "质量管理",
|
||||
"销售中心", "生产辅助", "科学排产", "文件夹", "其他"
|
||||
);
|
||||
// path 前缀映射
|
||||
Map<String, String> nameToPath = new LinkedHashMap<>();
|
||||
nameToPath.put("驾驶舱", "post");
|
||||
nameToPath.put("生产管控", "wip");
|
||||
nameToPath.put("数字仓储", "stock");
|
||||
nameToPath.put("质量管理", "quality");
|
||||
nameToPath.put("销售中心", "crm");
|
||||
nameToPath.put("生产辅助", "helper");
|
||||
nameToPath.put("科学排产", "aps");
|
||||
nameToPath.put("文件夹", "file");
|
||||
nameToPath.put("其他", "");
|
||||
|
||||
// 4. 构建 VO 列表
|
||||
List<OperMenuGroupVO> result = new ArrayList<>();
|
||||
for (String name : orderNames) {
|
||||
List<OperModuleStatVO> modules = groupMap.get(name);
|
||||
if (modules == null || modules.isEmpty()) {
|
||||
continue; // 没有数据的分类不展示
|
||||
}
|
||||
|
||||
OperMenuGroupVO vo = new OperMenuGroupVO();
|
||||
vo.setMenuName(name);
|
||||
vo.setMenuPath(nameToPath.getOrDefault(name, ""));
|
||||
|
||||
// 汇总该分类的总量
|
||||
long total = modules.stream().mapToLong(m -> m.getTotalCount() != null ? m.getTotalCount() : 0).sum();
|
||||
long success = modules.stream()
|
||||
.mapToLong(m -> Math.round((m.getTotalCount() != null ? m.getTotalCount() : 0)
|
||||
* (m.getSuccessRate() != null ? m.getSuccessRate() : 0) / 100.0))
|
||||
.sum();
|
||||
long fail = total - success;
|
||||
long persons = modules.stream()
|
||||
.mapToLong(m -> m.getPersonCount() != null ? m.getPersonCount() : 0).sum();
|
||||
// 去重人数更合理:取各模块人数最大值作为估算(无法精确去重)
|
||||
long distinctPersons = modules.stream()
|
||||
.mapToLong(m -> m.getPersonCount() != null ? m.getPersonCount() : 0)
|
||||
.max().orElse(0);
|
||||
// 用 distinct 人数更合理,取模块中最大人数(保守估计)
|
||||
// 实际跨模块可能有重复,这里用最大模块人数作为下限
|
||||
vo.setTotalCount(total);
|
||||
vo.setSuccessCount(success);
|
||||
vo.setFailCount(fail);
|
||||
vo.setSuccessRate(total > 0 ? Math.round(success * 10000.0 / total) / 100.0 : 0.0);
|
||||
vo.setPersonCount(distinctPersons);
|
||||
|
||||
// 模块明细按 totalCount 降序排列
|
||||
modules.sort((a, b) -> {
|
||||
long ta = a.getTotalCount() != null ? a.getTotalCount() : 0;
|
||||
long tb = b.getTotalCount() != null ? b.getTotalCount() : 0;
|
||||
return Long.compare(tb, ta);
|
||||
});
|
||||
vo.setModules(modules);
|
||||
|
||||
result.add(vo);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OperPersonVO> selectPersonPerformance(OperPerformanceQuery query) {
|
||||
// 1. 查询人员汇总
|
||||
|
||||
Reference in New Issue
Block a user