Compare commits
4 Commits
a2adaf38d1
...
e0f47ce1b6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0f47ce1b6 | ||
|
|
3ac1f69197 | ||
|
|
91393b7a57 | ||
|
|
a775809c4d |
@ -0,0 +1,221 @@
|
|||||||
|
package org.leocoder.thin.domain.model.vo.system;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仪表盘统计数据响应对象
|
||||||
|
*
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
public class DashboardStatisticsVo implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户统计数据
|
||||||
|
*/
|
||||||
|
@JsonProperty("userStats")
|
||||||
|
private UserStatsVo userStats;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录统计数据
|
||||||
|
*/
|
||||||
|
@JsonProperty("loginStats")
|
||||||
|
private LoginStatsVo loginStats;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储统计数据
|
||||||
|
*/
|
||||||
|
@JsonProperty("storageStats")
|
||||||
|
private StorageStatsVo storageStats;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 今日活跃统计数据
|
||||||
|
*/
|
||||||
|
@JsonProperty("dailyActivityStats")
|
||||||
|
private DailyActivityStatsVo dailyActivityStats;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户统计数据
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class UserStatsVo implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总用户数
|
||||||
|
*/
|
||||||
|
@JsonProperty("totalUsers")
|
||||||
|
private Integer totalUsers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 今日新增用户数
|
||||||
|
*/
|
||||||
|
@JsonProperty("todayNewUsers")
|
||||||
|
private Integer todayNewUsers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 活跃用户数
|
||||||
|
*/
|
||||||
|
@JsonProperty("activeUsers")
|
||||||
|
private Integer activeUsers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前在线用户数
|
||||||
|
*/
|
||||||
|
@JsonProperty("onlineUsers")
|
||||||
|
private Integer onlineUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录统计数据
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class LoginStatsVo implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 今日登录次数
|
||||||
|
*/
|
||||||
|
@JsonProperty("todayLogins")
|
||||||
|
private Integer todayLogins;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 累计登录次数
|
||||||
|
*/
|
||||||
|
@JsonProperty("totalLogins")
|
||||||
|
private Integer totalLogins;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录趋势数据
|
||||||
|
*/
|
||||||
|
@JsonProperty("loginTrend")
|
||||||
|
private List<LoginTrendItemVo> loginTrend;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储统计数据
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class StorageStatsVo implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总文件数
|
||||||
|
*/
|
||||||
|
@JsonProperty("totalFiles")
|
||||||
|
private Integer totalFiles;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总图片数
|
||||||
|
*/
|
||||||
|
@JsonProperty("totalImages")
|
||||||
|
private Integer totalImages;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总存储大小(格式化)
|
||||||
|
*/
|
||||||
|
@JsonProperty("totalSize")
|
||||||
|
private String totalSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 今日上传文件数
|
||||||
|
*/
|
||||||
|
@JsonProperty("todayUploads")
|
||||||
|
private Integer todayUploads;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储使用率(百分比)
|
||||||
|
*/
|
||||||
|
@JsonProperty("storageUsage")
|
||||||
|
private Double storageUsage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可用空间(格式化)
|
||||||
|
*/
|
||||||
|
@JsonProperty("availableSpace")
|
||||||
|
private String availableSpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 今日活跃统计数据
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class DailyActivityStatsVo implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 今日访问量
|
||||||
|
*/
|
||||||
|
@JsonProperty("todayVisits")
|
||||||
|
private Integer todayVisits;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 今日操作数
|
||||||
|
*/
|
||||||
|
@JsonProperty("todayOperations")
|
||||||
|
private Integer todayOperations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 活跃用户数
|
||||||
|
*/
|
||||||
|
@JsonProperty("activeUsers")
|
||||||
|
private Integer activeUsers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增内容数
|
||||||
|
*/
|
||||||
|
@JsonProperty("newContent")
|
||||||
|
private Integer newContent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API调用次数
|
||||||
|
*/
|
||||||
|
@JsonProperty("apiCalls")
|
||||||
|
private Integer apiCalls;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平均响应时间(毫秒)
|
||||||
|
*/
|
||||||
|
@JsonProperty("avgResponseTime")
|
||||||
|
private Integer avgResponseTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录趋势项
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class LoginTrendItemVo implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日期(YYYY-MM-DD格式)
|
||||||
|
*/
|
||||||
|
@JsonProperty("date")
|
||||||
|
private String date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当日登录次数
|
||||||
|
*/
|
||||||
|
@JsonProperty("count")
|
||||||
|
private Integer count;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示标签(用于图表展示)
|
||||||
|
*/
|
||||||
|
@JsonProperty("label")
|
||||||
|
private String label;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
package org.leocoder.thin.domain.model.vo.system;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录趋势数据响应对象
|
||||||
|
*
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
public class LoginTrendVo implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录趋势数据列表
|
||||||
|
*/
|
||||||
|
@JsonProperty("loginTrend")
|
||||||
|
private List<LoginTrendItemVo> loginTrend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录趋势项
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class LoginTrendItemVo implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日期(YYYY-MM-DD格式)
|
||||||
|
*/
|
||||||
|
@JsonProperty("date")
|
||||||
|
private String date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当日登录次数
|
||||||
|
*/
|
||||||
|
@JsonProperty("count")
|
||||||
|
private Integer count;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示标签(用于图表展示)
|
||||||
|
*/
|
||||||
|
@JsonProperty("label")
|
||||||
|
private String label;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,173 @@
|
|||||||
|
package org.leocoder.thin.system.controller.dashboard;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
import org.leocoder.thin.common.exception.coder.YUtil;
|
||||||
|
import org.leocoder.thin.domain.model.vo.system.DashboardStatisticsVo;
|
||||||
|
import org.leocoder.thin.domain.model.vo.system.LoginTrendVo;
|
||||||
|
import org.leocoder.thin.system.service.dashboard.DashboardService;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仪表盘统计控制器
|
||||||
|
*
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Tag(name = "仪表盘管理", description = "仪表盘统计数据接口")
|
||||||
|
@Slf4j
|
||||||
|
@Validated
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/coder/dashboard")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DashboardController {
|
||||||
|
|
||||||
|
private final DashboardService dashboardService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取仪表盘统计数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Operation(
|
||||||
|
summary = "获取仪表盘统计数据",
|
||||||
|
description = "获取用户、登录、存储、活跃度等核心统计数据"
|
||||||
|
)
|
||||||
|
@GetMapping("/getStatistics")
|
||||||
|
@SaCheckPermission("dashboard:view")
|
||||||
|
public DashboardStatisticsVo getStatistics() {
|
||||||
|
DashboardStatisticsVo statistics = dashboardService.getStatistics();
|
||||||
|
YUtil.isTrue(ObjectUtils.isEmpty(statistics), "获取仪表盘统计数据失败");
|
||||||
|
return statistics;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取登录趋势数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Operation(
|
||||||
|
summary = "获取登录趋势数据",
|
||||||
|
description = "获取最近N天的登录趋势图表数据"
|
||||||
|
)
|
||||||
|
@GetMapping("/getLoginTrend")
|
||||||
|
@SaCheckPermission("dashboard:view")
|
||||||
|
public LoginTrendVo getLoginTrend(
|
||||||
|
@Parameter(description = "查询天数", example = "7") @RequestParam(
|
||||||
|
defaultValue = "7"
|
||||||
|
) Integer days
|
||||||
|
) {
|
||||||
|
LoginTrendVo loginTrend = dashboardService.getLoginTrend(days);
|
||||||
|
YUtil.isTrue(ObjectUtils.isEmpty(loginTrend), "获取登录趋势数据失败");
|
||||||
|
return loginTrend;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取完整仪表盘数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Operation(
|
||||||
|
summary = "获取完整仪表盘数据",
|
||||||
|
description = "一次性获取所有仪表盘数据,减少前端请求次数"
|
||||||
|
)
|
||||||
|
@GetMapping("/getAllData")
|
||||||
|
@SaCheckPermission("dashboard:view")
|
||||||
|
public DashboardStatisticsVo getAllData(
|
||||||
|
@Parameter(
|
||||||
|
description = "是否包含趋势数据",
|
||||||
|
example = "true"
|
||||||
|
) @RequestParam(defaultValue = "true") Boolean includeTrend,
|
||||||
|
@Parameter(description = "趋势数据天数", example = "7") @RequestParam(
|
||||||
|
defaultValue = "7"
|
||||||
|
) Integer trendDays
|
||||||
|
) {
|
||||||
|
DashboardStatisticsVo allData = dashboardService.getAllData(
|
||||||
|
includeTrend,
|
||||||
|
trendDays
|
||||||
|
);
|
||||||
|
YUtil.isTrue(ObjectUtils.isEmpty(allData), "获取完整仪表盘数据失败");
|
||||||
|
return allData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取用户统计数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Operation(
|
||||||
|
summary = "获取用户统计数据",
|
||||||
|
description = "获取用户相关的统计信息"
|
||||||
|
)
|
||||||
|
@GetMapping("/getUserStats")
|
||||||
|
@SaCheckPermission("dashboard:view")
|
||||||
|
public DashboardStatisticsVo.UserStatsVo getUserStats() {
|
||||||
|
DashboardStatisticsVo.UserStatsVo userStats =
|
||||||
|
dashboardService.getUserStats();
|
||||||
|
YUtil.isTrue(ObjectUtils.isEmpty(userStats), "获取用户统计数据失败");
|
||||||
|
return userStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取登录统计数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Operation(
|
||||||
|
summary = "获取登录统计数据",
|
||||||
|
description = "获取登录相关的统计信息"
|
||||||
|
)
|
||||||
|
@GetMapping("/getLoginStats")
|
||||||
|
@SaCheckPermission("dashboard:view")
|
||||||
|
public DashboardStatisticsVo.LoginStatsVo getLoginStats(
|
||||||
|
@Parameter(
|
||||||
|
description = "是否包含趋势数据",
|
||||||
|
example = "false"
|
||||||
|
) @RequestParam(defaultValue = "false") Boolean includeTrend,
|
||||||
|
@Parameter(description = "趋势数据天数", example = "7") @RequestParam(
|
||||||
|
defaultValue = "7"
|
||||||
|
) Integer trendDays
|
||||||
|
) {
|
||||||
|
DashboardStatisticsVo.LoginStatsVo loginStats =
|
||||||
|
dashboardService.getLoginStats(includeTrend, trendDays);
|
||||||
|
YUtil.isTrue(ObjectUtils.isEmpty(loginStats), "获取登录统计数据失败");
|
||||||
|
return loginStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取存储统计数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Operation(
|
||||||
|
summary = "获取存储统计数据",
|
||||||
|
description = "获取存储相关的统计信息"
|
||||||
|
)
|
||||||
|
@GetMapping("/getStorageStats")
|
||||||
|
@SaCheckPermission("dashboard:view")
|
||||||
|
public DashboardStatisticsVo.StorageStatsVo getStorageStats() {
|
||||||
|
DashboardStatisticsVo.StorageStatsVo storageStats =
|
||||||
|
dashboardService.getStorageStats();
|
||||||
|
YUtil.isTrue(ObjectUtils.isEmpty(storageStats), "获取存储统计数据失败");
|
||||||
|
return storageStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取今日活跃统计数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Operation(
|
||||||
|
summary = "获取今日活跃统计数据",
|
||||||
|
description = "获取今日活跃相关的统计信息"
|
||||||
|
)
|
||||||
|
@GetMapping("/getDailyActivityStats")
|
||||||
|
@SaCheckPermission("dashboard:view")
|
||||||
|
public DashboardStatisticsVo.DailyActivityStatsVo getDailyActivityStats() {
|
||||||
|
DashboardStatisticsVo.DailyActivityStatsVo activityStats =
|
||||||
|
dashboardService.getDailyActivityStats();
|
||||||
|
YUtil.isTrue(
|
||||||
|
ObjectUtils.isEmpty(activityStats),
|
||||||
|
"获取今日活跃统计数据失败"
|
||||||
|
);
|
||||||
|
return activityStats;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
package org.leocoder.thin.system.service.dashboard;
|
||||||
|
|
||||||
|
import org.leocoder.thin.domain.model.vo.system.DashboardStatisticsVo;
|
||||||
|
import org.leocoder.thin.domain.model.vo.system.LoginTrendVo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仪表盘统计服务接口
|
||||||
|
*
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
public interface DashboardService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取仪表盘统计数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
DashboardStatisticsVo getStatistics();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取登录趋势数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
LoginTrendVo getLoginTrend(Integer days);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取完整仪表盘数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
DashboardStatisticsVo getAllData(Boolean includeTrend, Integer trendDays);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取用户统计数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
DashboardStatisticsVo.UserStatsVo getUserStats();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取登录统计数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
DashboardStatisticsVo.LoginStatsVo getLoginStats(Boolean includeTrend, Integer trendDays);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取存储统计数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
DashboardStatisticsVo.StorageStatsVo getStorageStats();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取今日活跃统计数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
DashboardStatisticsVo.DailyActivityStatsVo getDailyActivityStats();
|
||||||
|
}
|
||||||
@ -0,0 +1,370 @@
|
|||||||
|
package org.leocoder.thin.system.service.dashboard;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.leocoder.thin.domain.model.vo.system.DashboardStatisticsVo;
|
||||||
|
import org.leocoder.thin.domain.model.vo.system.LoginTrendVo;
|
||||||
|
import org.leocoder.thin.domain.pojo.system.*;
|
||||||
|
import org.leocoder.thin.mybatisplus.mapper.system.*;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仪表盘统计服务实现类
|
||||||
|
*
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DashboardServiceImpl implements DashboardService {
|
||||||
|
|
||||||
|
private final SysLoginUserMapper sysLoginUserMapper;
|
||||||
|
private final SysLoginLogMapper sysLoginLogMapper;
|
||||||
|
private final SysOperLogMapper sysOperLogMapper;
|
||||||
|
private final SysFileMapper sysFileMapper;
|
||||||
|
private final SysPictureMapper sysPictureMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取仪表盘统计数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public DashboardStatisticsVo getStatistics() {
|
||||||
|
DashboardStatisticsVo statisticsVo = new DashboardStatisticsVo();
|
||||||
|
|
||||||
|
// 设置用户统计数据
|
||||||
|
statisticsVo.setUserStats(getUserStats());
|
||||||
|
|
||||||
|
// 设置登录统计数据(不包含趋势)
|
||||||
|
statisticsVo.setLoginStats(getLoginStats(false, null));
|
||||||
|
|
||||||
|
// 设置存储统计数据
|
||||||
|
statisticsVo.setStorageStats(getStorageStats());
|
||||||
|
|
||||||
|
// 设置今日活跃统计数据
|
||||||
|
statisticsVo.setDailyActivityStats(getDailyActivityStats());
|
||||||
|
|
||||||
|
return statisticsVo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取登录趋势数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public LoginTrendVo getLoginTrend(Integer days) {
|
||||||
|
if (days == null || days <= 0) {
|
||||||
|
days = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoginTrendVo loginTrendVo = new LoginTrendVo();
|
||||||
|
List<LoginTrendVo.LoginTrendItemVo> trendList = new ArrayList<>();
|
||||||
|
|
||||||
|
LocalDate today = LocalDate.now();
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
|
DateTimeFormatter labelFormatter = DateTimeFormatter.ofPattern(
|
||||||
|
"M月d日"
|
||||||
|
);
|
||||||
|
|
||||||
|
for (int i = days - 1; i >= 0; i--) {
|
||||||
|
LocalDate date = today.minusDays(i);
|
||||||
|
String dateStr = date.format(formatter);
|
||||||
|
String label = date.format(labelFormatter);
|
||||||
|
|
||||||
|
// 统计该日的登录次数
|
||||||
|
LambdaQueryWrapper<SysLoginLog> wrapper =
|
||||||
|
new LambdaQueryWrapper<>();
|
||||||
|
wrapper
|
||||||
|
.apply("DATE(login_time) = {0}", dateStr)
|
||||||
|
// 成功登录
|
||||||
|
.eq(SysLoginLog::getLoginStatus, "0");
|
||||||
|
Long count = sysLoginLogMapper.selectCount(wrapper);
|
||||||
|
|
||||||
|
LoginTrendVo.LoginTrendItemVo trendItem =
|
||||||
|
new LoginTrendVo.LoginTrendItemVo();
|
||||||
|
trendItem.setDate(dateStr);
|
||||||
|
trendItem.setCount(count != null ? count.intValue() : 0);
|
||||||
|
trendItem.setLabel(label);
|
||||||
|
|
||||||
|
trendList.add(trendItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
loginTrendVo.setLoginTrend(trendList);
|
||||||
|
return loginTrendVo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取完整仪表盘数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public DashboardStatisticsVo getAllData(
|
||||||
|
Boolean includeTrend,
|
||||||
|
Integer trendDays
|
||||||
|
) {
|
||||||
|
DashboardStatisticsVo statisticsVo = new DashboardStatisticsVo();
|
||||||
|
|
||||||
|
// 设置用户统计数据
|
||||||
|
statisticsVo.setUserStats(getUserStats());
|
||||||
|
|
||||||
|
// 设置登录统计数据(根据参数决定是否包含趋势)
|
||||||
|
statisticsVo.setLoginStats(getLoginStats(includeTrend, trendDays));
|
||||||
|
|
||||||
|
// 设置存储统计数据
|
||||||
|
statisticsVo.setStorageStats(getStorageStats());
|
||||||
|
|
||||||
|
// 设置今日活跃统计数据
|
||||||
|
statisticsVo.setDailyActivityStats(getDailyActivityStats());
|
||||||
|
|
||||||
|
return statisticsVo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取用户统计数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public DashboardStatisticsVo.UserStatsVo getUserStats() {
|
||||||
|
DashboardStatisticsVo.UserStatsVo userStats =
|
||||||
|
new DashboardStatisticsVo.UserStatsVo();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 总用户数
|
||||||
|
Long totalUsers = sysLoginUserMapper.selectCount(null);
|
||||||
|
userStats.setTotalUsers(
|
||||||
|
totalUsers != null ? totalUsers.intValue() : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
// 今日新增用户数
|
||||||
|
LambdaQueryWrapper<SysLoginUser> todayWrapper =
|
||||||
|
new LambdaQueryWrapper<>();
|
||||||
|
todayWrapper.apply("DATE(create_time) = CURDATE()");
|
||||||
|
Long todayNewUsers = sysLoginUserMapper.selectCount(todayWrapper);
|
||||||
|
userStats.setTodayNewUsers(
|
||||||
|
todayNewUsers != null ? todayNewUsers.intValue() : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
// 活跃用户数(最近30天有登录记录的用户)
|
||||||
|
LambdaQueryWrapper<SysLoginLog> activeWrapper =
|
||||||
|
new LambdaQueryWrapper<>();
|
||||||
|
activeWrapper
|
||||||
|
.apply("login_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)")
|
||||||
|
.eq(SysLoginLog::getLoginStatus, "0")
|
||||||
|
.select(SysLoginLog::getLoginName)
|
||||||
|
.groupBy(SysLoginLog::getLoginName);
|
||||||
|
Long activeUsers = sysLoginLogMapper.selectCount(activeWrapper);
|
||||||
|
userStats.setActiveUsers(
|
||||||
|
activeUsers != null ? activeUsers.intValue() : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
// 当前在线用户数(通过Sa-Token获取)
|
||||||
|
List<String> onlineTokens = StpUtil.searchTokenValue(
|
||||||
|
"",
|
||||||
|
0,
|
||||||
|
-1,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
userStats.setOnlineUsers(
|
||||||
|
onlineTokens != null ? onlineTokens.size() : 0
|
||||||
|
);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取用户统计数据失败", e);
|
||||||
|
// 设置默认值
|
||||||
|
userStats.setTotalUsers(0);
|
||||||
|
userStats.setTodayNewUsers(0);
|
||||||
|
userStats.setActiveUsers(0);
|
||||||
|
userStats.setOnlineUsers(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return userStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取登录统计数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public DashboardStatisticsVo.LoginStatsVo getLoginStats(
|
||||||
|
Boolean includeTrend,
|
||||||
|
Integer trendDays
|
||||||
|
) {
|
||||||
|
DashboardStatisticsVo.LoginStatsVo loginStats =
|
||||||
|
new DashboardStatisticsVo.LoginStatsVo();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 今日登录次数
|
||||||
|
LambdaQueryWrapper<SysLoginLog> todayWrapper =
|
||||||
|
new LambdaQueryWrapper<>();
|
||||||
|
todayWrapper
|
||||||
|
.apply("DATE(login_time) = CURDATE()")
|
||||||
|
.eq(SysLoginLog::getLoginStatus, "0"); // 成功登录
|
||||||
|
Long todayLogins = sysLoginLogMapper.selectCount(todayWrapper);
|
||||||
|
loginStats.setTodayLogins(
|
||||||
|
todayLogins != null ? todayLogins.intValue() : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
// 累计登录次数
|
||||||
|
LambdaQueryWrapper<SysLoginLog> totalWrapper =
|
||||||
|
new LambdaQueryWrapper<>();
|
||||||
|
totalWrapper.eq(SysLoginLog::getLoginStatus, "0"); // 成功登录
|
||||||
|
Long totalLogins = sysLoginLogMapper.selectCount(totalWrapper);
|
||||||
|
loginStats.setTotalLogins(
|
||||||
|
totalLogins != null ? totalLogins.intValue() : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
// 根据参数决定是否包含趋势数据
|
||||||
|
if (Boolean.TRUE.equals(includeTrend)) {
|
||||||
|
LoginTrendVo trendVo = getLoginTrend(trendDays);
|
||||||
|
// 转换LoginTrendItemVo类型
|
||||||
|
if (trendVo.getLoginTrend() != null) {
|
||||||
|
List<DashboardStatisticsVo.LoginTrendItemVo> trendItems =
|
||||||
|
new ArrayList<>();
|
||||||
|
for (LoginTrendVo.LoginTrendItemVo item : trendVo.getLoginTrend()) {
|
||||||
|
DashboardStatisticsVo.LoginTrendItemVo trendItem =
|
||||||
|
new DashboardStatisticsVo.LoginTrendItemVo();
|
||||||
|
trendItem.setDate(item.getDate());
|
||||||
|
trendItem.setCount(item.getCount());
|
||||||
|
trendItem.setLabel(item.getLabel());
|
||||||
|
trendItems.add(trendItem);
|
||||||
|
}
|
||||||
|
loginStats.setLoginTrend(trendItems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取登录统计数据失败", e);
|
||||||
|
// 设置默认值
|
||||||
|
loginStats.setTodayLogins(0);
|
||||||
|
loginStats.setTotalLogins(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return loginStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取存储统计数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public DashboardStatisticsVo.StorageStatsVo getStorageStats() {
|
||||||
|
DashboardStatisticsVo.StorageStatsVo storageStats =
|
||||||
|
new DashboardStatisticsVo.StorageStatsVo();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 总文件数
|
||||||
|
Long totalFiles = sysFileMapper.selectCount(null);
|
||||||
|
storageStats.setTotalFiles(
|
||||||
|
totalFiles != null ? totalFiles.intValue() : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
// 总图片数
|
||||||
|
Long totalImages = sysPictureMapper.selectCount(null);
|
||||||
|
storageStats.setTotalImages(
|
||||||
|
totalImages != null ? totalImages.intValue() : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
// 今日上传文件数
|
||||||
|
LambdaQueryWrapper<SysFile> todayWrapper =
|
||||||
|
new LambdaQueryWrapper<>();
|
||||||
|
todayWrapper.apply("DATE(create_time) = CURDATE()");
|
||||||
|
Long todayUploads = sysFileMapper.selectCount(todayWrapper);
|
||||||
|
storageStats.setTodayUploads(
|
||||||
|
todayUploads != null ? todayUploads.intValue() : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
// 计算总存储大小(这里可以根据实际情况优化)
|
||||||
|
// 由于文件大小计算比较复杂,这里先设置示例数据
|
||||||
|
storageStats.setTotalSize("2.3 GB");
|
||||||
|
storageStats.setStorageUsage(67.5);
|
||||||
|
storageStats.setAvailableSpace("1.2 GB");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取存储统计数据失败", e);
|
||||||
|
// 设置默认值
|
||||||
|
storageStats.setTotalFiles(0);
|
||||||
|
storageStats.setTotalImages(0);
|
||||||
|
storageStats.setTodayUploads(0);
|
||||||
|
storageStats.setTotalSize("0 B");
|
||||||
|
storageStats.setStorageUsage(0.0);
|
||||||
|
storageStats.setAvailableSpace("0 B");
|
||||||
|
}
|
||||||
|
|
||||||
|
return storageStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取今日活跃统计数据
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public DashboardStatisticsVo.DailyActivityStatsVo getDailyActivityStats() {
|
||||||
|
DashboardStatisticsVo.DailyActivityStatsVo activityStats =
|
||||||
|
new DashboardStatisticsVo.DailyActivityStatsVo();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 今日访问量
|
||||||
|
LambdaQueryWrapper<SysOperLog> todayWrapper =
|
||||||
|
new LambdaQueryWrapper<>();
|
||||||
|
todayWrapper.apply("DATE(oper_time) = CURDATE()");
|
||||||
|
Long todayVisits = sysOperLogMapper.selectCount(todayWrapper);
|
||||||
|
activityStats.setTodayVisits(
|
||||||
|
todayVisits != null ? todayVisits.intValue() : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
// 今日操作数(排除查询操作)
|
||||||
|
LambdaQueryWrapper<SysOperLog> operWrapper =
|
||||||
|
new LambdaQueryWrapper<>();
|
||||||
|
operWrapper
|
||||||
|
.apply("DATE(oper_time) = CURDATE()")
|
||||||
|
// 排除查询操作
|
||||||
|
.ne(SysOperLog::getOperType, "SELECT");
|
||||||
|
Long todayOperations = sysOperLogMapper.selectCount(operWrapper);
|
||||||
|
activityStats.setTodayOperations(
|
||||||
|
todayOperations != null ? todayOperations.intValue() : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
// 活跃用户数(取用户统计中的活跃用户数)
|
||||||
|
DashboardStatisticsVo.UserStatsVo userStats = getUserStats();
|
||||||
|
activityStats.setActiveUsers(userStats.getActiveUsers());
|
||||||
|
|
||||||
|
// 新增内容数(今日新增的文件和图片)
|
||||||
|
LambdaQueryWrapper<SysFile> fileWrapper =
|
||||||
|
new LambdaQueryWrapper<>();
|
||||||
|
fileWrapper.apply("DATE(create_time) = CURDATE()");
|
||||||
|
Long newFiles = sysFileMapper.selectCount(fileWrapper);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<SysPicture> pictureWrapper =
|
||||||
|
new LambdaQueryWrapper<>();
|
||||||
|
pictureWrapper.apply("DATE(create_time) = CURDATE()");
|
||||||
|
Long newPictures = sysPictureMapper.selectCount(pictureWrapper);
|
||||||
|
|
||||||
|
int newContent =
|
||||||
|
(newFiles != null ? newFiles.intValue() : 0) +
|
||||||
|
(newPictures != null ? newPictures.intValue() : 0);
|
||||||
|
activityStats.setNewContent(newContent);
|
||||||
|
|
||||||
|
// API调用次数(今日操作日志总数)
|
||||||
|
activityStats.setApiCalls(activityStats.getTodayVisits());
|
||||||
|
|
||||||
|
// 平均响应时间(这里可以根据实际情况计算)
|
||||||
|
// 示例数据
|
||||||
|
activityStats.setAvgResponseTime(235);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取今日活跃统计数据失败", e);
|
||||||
|
// 设置默认值
|
||||||
|
activityStats.setTodayVisits(0);
|
||||||
|
activityStats.setTodayOperations(0);
|
||||||
|
activityStats.setActiveUsers(0);
|
||||||
|
activityStats.setNewContent(0);
|
||||||
|
activityStats.setApiCalls(0);
|
||||||
|
activityStats.setAvgResponseTime(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return activityStats;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -36,6 +36,14 @@ public class ResultResponseHandler implements ResponseBodyAdvice<Object> {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean supports(@Nullable MethodParameter methodParameter, @Nullable Class<? extends HttpMessageConverter<?>> CoderClass) {
|
public boolean supports(@Nullable MethodParameter methodParameter, @Nullable Class<? extends HttpMessageConverter<?>> CoderClass) {
|
||||||
|
// 排除Swagger相关路径,避免干扰OpenAPI文档生成
|
||||||
|
if (methodParameter != null && methodParameter.getMethod() != null) {
|
||||||
|
String className = methodParameter.getMethod().getDeclaringClass().getName();
|
||||||
|
// 排除SpringDoc相关的Controller
|
||||||
|
if (className.contains("springdoc") || className.contains("swagger")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,6 +52,15 @@ public class ResultResponseHandler implements ResponseBodyAdvice<Object> {
|
|||||||
// 参数body 代表其实就是SpringMvc的请求的方法的结果
|
// 参数body 代表其实就是SpringMvc的请求的方法的结果
|
||||||
// 对请求的结果在这里统一返回和处理
|
// 对请求的结果在这里统一返回和处理
|
||||||
|
|
||||||
|
// 排除Swagger相关路径,避免包装OpenAPI文档
|
||||||
|
String requestPath = serverHttpRequest.getURI().getPath();
|
||||||
|
if (requestPath.startsWith("/v3/api-docs") ||
|
||||||
|
requestPath.startsWith("/swagger-ui") ||
|
||||||
|
requestPath.contains("/swagger") ||
|
||||||
|
requestPath.contains("/api-docs")) {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
if (body instanceof ErrorHandler errorHandler) {
|
if (body instanceof ErrorHandler errorHandler) {
|
||||||
// 如果返回的结果是一个异常的结果,就把异常返回的结构数据倒腾到R.error里面即可
|
// 如果返回的结果是一个异常的结果,就把异常返回的结构数据倒腾到R.error里面即可
|
||||||
return ResultUtils.error(errorHandler.getStatus(), errorHandler.getMsg());
|
return ResultUtils.error(errorHandler.getStatus(), errorHandler.getMsg());
|
||||||
|
|||||||
@ -52,6 +52,12 @@ public class CoderSaTokenInterceptor implements WebMvcConfigurer {
|
|||||||
// ignoreUrls.add("/**/*.js");
|
// ignoreUrls.add("/**/*.js");
|
||||||
// 上传路径
|
// 上传路径
|
||||||
ignoreUrls.add(baseFilePath + "/**");
|
ignoreUrls.add(baseFilePath + "/**");
|
||||||
|
// Swagger API文档相关路径
|
||||||
|
ignoreUrls.add("/swagger-ui/**");
|
||||||
|
ignoreUrls.add("/v3/api-docs/**");
|
||||||
|
ignoreUrls.add("/v3/api-docs");
|
||||||
|
ignoreUrls.add("/swagger-ui.html");
|
||||||
|
ignoreUrls.add("/webjars/**");
|
||||||
|
|
||||||
// 除白名单路径外均需要登录认证
|
// 除白名单路径外均需要登录认证
|
||||||
SaRouter.match("/**").notMatch(ignoreUrls).check(r -> StpUtil.checkLogin());
|
SaRouter.match("/**").notMatch(ignoreUrls).check(r -> StpUtil.checkLogin());
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user