overview统计

This commit is contained in:
2025-11-14 18:31:39 +08:00
parent 6be3cc6abd
commit 9adc0c2058
24 changed files with 723 additions and 178 deletions

View File

@@ -183,4 +183,7 @@ public interface ResourceService {
* @since 2025-10-15
*/
ResultDomain<TbResource> searchResources(String keyword, String tagID, Integer status);
ResultDomain<Integer> getResourceCount(TbResource filter);
}

View File

@@ -190,4 +190,13 @@ public interface CourseService {
* @since 2025-10-28
*/
ResultDomain<CourseItemVO> getCourseProgress(String courseID);
/**
* @description 获取课程数量
* @param filter 过滤条件
* @return ResultDomain<Integer> 课程数量
* @author yslg
* @since 2025-11-14
*/
ResultDomain<Integer> getCourseCount(TbCourse filter);
}

View File

@@ -237,4 +237,13 @@ public interface LearningTaskService {
* @since 2025-10-30
*/
ResultDomain<Map<String, Object>> getTaskStatisticsRankings(String taskID);
/**
* @description 获取学习任务数量
* @param filter 过滤条件
* @return ResultDomain<Integer> 学习任务数量
* @author yslg
* @since 2025-11-14
*/
ResultDomain<Integer> getLearningTaskCount(TbLearningTask filter);
}

View File

@@ -18,6 +18,8 @@ import org.xyzh.common.core.domain.LoginDomain;
import org.xyzh.common.redis.service.RedisService;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -34,6 +36,11 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
private static final String REDIS_LOGIN_PREFIX = "login:token:";
/**
* @description UV统计key前缀例如 stat:uv:20251114
*/
private static final String REDIS_UV_PREFIX = "stat:uv:";
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
@Autowired
@@ -89,8 +96,17 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
logger.debug("用户认证成功从缓存userId: {}", userId);
// 记录UV以用户ID为维度按天去重
try {
String today = LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
String uvKey = REDIS_UV_PREFIX + today;
redisService.sAdd(uvKey, userId);
} catch (Exception e) {
logger.warn("记录UV失败, userId={}: {}", userId, e.getMessage());
}
} else {
logger.warn("Redis缓存中未找到用户登录信息userId: {}, 可能已过期或未登录", userId);
}

View File

@@ -28,6 +28,10 @@ public class BaseDTO implements Serializable{
*/
private Date createTime;
private Date startTime;
private Date endTime;
/**
* @description 更新时间
* @author yslg
@@ -94,6 +98,22 @@ public class BaseDTO implements Serializable{
this.createTime = createTime;
}
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
/**
* @description 获取更新时间
* @return Date 更新时间

View File

@@ -283,6 +283,18 @@ public class RedisService {
return redisTemplate.opsForSet().members(key);
}
/**
* @description 获取Set集合大小等价于Redis的SCARD
* @param key String 键
* @return long 集合元素数量
* @author yslg
* @since 2025-11-14
*/
public long sCard(String key) {
Long size = redisTemplate.opsForSet().size(key);
return size != null ? size : 0L;
}
/**
* @description ZSet操作-添加元素
* @param key String 键

View File

@@ -294,4 +294,29 @@ public class TimeUtils {
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}
/**
* @description 获取昨天的开始时间
* @return java.util.Date对象
* @author yslg
* @since 2025-11-14
*/
public static Date getStartTimeOfYesterday() {
LocalDate yesterday = LocalDate.now().minusDays(1);
return Date.from(
yesterday.atStartOfDay(ZoneId.systemDefault()).toInstant()
);
}
/**
* @description 获取昨天的结束时间
* @return java.util.Date对象
* @author yslg
* @since 2025-11-14
*/
public static Date getEndTimeOfYesterday() {
LocalDate today = LocalDate.now();
return Date.from(
today.atStartOfDay(ZoneId.systemDefault()).minusNanos(1).toInstant()
);
}
}

View File

@@ -754,4 +754,19 @@ public class NCResourceServiceImpl implements ResourceService {
return resultDomain;
}
}
@Override
public ResultDomain<Integer> getResourceCount(TbResource filter) {
ResultDomain<Integer> resultDomain = new ResultDomain<>();
try {
List<UserDeptRoleVO> userDeptRoles = LoginUtil.getCurrentDeptRole();
long count = resourceMapper.countResources(filter, userDeptRoles);
resultDomain.success("获取资源总数成功", (int)count);
return resultDomain;
} catch (Exception e) {
logger.error("获取资源总数异常: {}", e.getMessage(), e);
resultDomain.fail("获取资源总数失败: " + e.getMessage());
return resultDomain;
}
}
}

View File

@@ -387,6 +387,12 @@
<if test="filter.isBanner != null">
AND r.is_banner = #{filter.isBanner}
</if>
<if test="filter.startTime != null">
AND r.publish_time &gt;= #{filter.startTime}
</if>
<if test="filter.endTime != null">
AND r.publish_time &lt;= #{filter.endTime}
</if>
</select>
<!-- updateResourceCollectCount -->

View File

@@ -550,14 +550,14 @@ public class SCCourseServiceImpl implements SCCourseService {
@Override
public ResultDomain<CourseItemVO> getCourseProgress(String courseID) {
ResultDomain<CourseItemVO> resultDomain = new ResultDomain<>();
// 获取当前用户
TbSysUser user = LoginUtil.getCurrentUser();
if (user == null) {
resultDomain.fail("用户未登录");
return resultDomain;
}
// 查询课程
TbCourse course = courseMapper.selectByCourseId(courseID);
if (course == null) {
@@ -572,25 +572,25 @@ public class SCCourseServiceImpl implements SCCourseService {
TbCourseChapter filter = new TbCourseChapter();
filter.setCourseID(courseID);
List<TbCourseChapter> chapters = courseChapterMapper.selectCourseChapters(filter);
// 查询并构建章节及节点结构(带进度)
if (!chapters.isEmpty()) {
List<String> chapterIDs = chapters.stream()
.map(TbCourseChapter::getChapterID)
.collect(Collectors.toList());
// 查询带进度的节点传入用户ID
List<CourseItemVO> nodesWithProgress = courseNodeMapper.selectNodesProgress(chapterIDs, user.getID());
// 转换章节为CourseItemVO列表
List<CourseItemVO> chapterVOs = chapters.stream()
.map(CourseItemVO::fromChapter)
.collect(Collectors.toList());
// 按章节ID分组节点
Map<String, List<CourseItemVO>> nodesMap = nodesWithProgress.stream()
.collect(Collectors.groupingBy(CourseItemVO::getChapterID));
// 设置章节列表和章节节点映射
courseItemVO.setChapters(chapterVOs);
courseItemVO.setChapterNodes(nodesMap);
@@ -599,4 +599,19 @@ public class SCCourseServiceImpl implements SCCourseService {
resultDomain.success("获取课程进度成功", courseItemVO);
return resultDomain;
}
@Override
public ResultDomain<Integer> getCourseCount(TbCourse filter) {
ResultDomain<Integer> resultDomain = new ResultDomain<>();
try {
// 获取当前用户的部门角色
List<UserDeptRoleVO> userDeptRoles = LoginUtil.getCurrentDeptRole();
long count = courseMapper.countCourses(filter, userDeptRoles);
resultDomain.success("获取课程数量成功", (int) count);
} catch (Exception e) {
logger.error("获取课程数量失败", e);
resultDomain.fail("获取课程数量失败: " + e.getMessage());
}
return resultDomain;
}
}

View File

@@ -796,15 +796,15 @@ public class SCLearningTaskServiceImpl implements LearningTaskService {
ResultDomain<Map<String, Object>> resultDomain = new ResultDomain<>();
try {
Map<String, Object> rankingsData = new HashMap<>();
// 获取完成时间排行榜前10名
List<Map<String, Object>> completionTimeRanking = taskUserMapper.getCompletionTimeRanking(taskID);
rankingsData.put("completionTimeRanking", completionTimeRanking);
// 获取学习时长排行榜前10名
List<Map<String, Object>> durationRanking = taskUserMapper.getStudyDurationRanking(taskID);
rankingsData.put("durationRanking", durationRanking);
resultDomain.success("获取任务排行榜数据成功", rankingsData);
} catch (Exception e) {
logger.error("获取任务排行榜数据失败", e);
@@ -812,4 +812,19 @@ public class SCLearningTaskServiceImpl implements LearningTaskService {
}
return resultDomain;
}
@Override
public ResultDomain<Integer> getLearningTaskCount(TbLearningTask filter) {
ResultDomain<Integer> resultDomain = new ResultDomain<>();
try {
// 获取当前用户的部门角色
List<UserDeptRoleVO> userDeptRoles = LoginUtil.getCurrentDeptRole();
long count = learningTaskMapper.countLearningTasks(filter, userDeptRoles);
resultDomain.success("获取学习任务数量成功", (int) count);
} catch (Exception e) {
logger.error("获取学习任务数量失败", e);
resultDomain.fail("获取学习任务数量失败: " + e.getMessage());
}
return resultDomain;
}
}

View File

@@ -25,6 +25,16 @@
<artifactId>api-system</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.xyzh</groupId>
<artifactId>api-news</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.xyzh</groupId>
<artifactId>api-study</artifactId>
<version>1.0.0</version>
</dependency>
<!-- Common模块依赖 -->
<dependency>

View File

@@ -2,17 +2,34 @@ package org.xyzh.system.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.xyzh.api.news.resource.ResourceService;
import org.xyzh.api.study.course.CourseService;
import org.xyzh.api.study.task.LearningTaskService;
import org.xyzh.common.core.domain.ResultDomain;
import java.time.LocalDate;
import org.xyzh.common.dto.resource.TbResource;
import org.xyzh.common.dto.study.TbCourse;
import org.xyzh.common.dto.study.TbLearningTask;
import org.xyzh.common.dto.system.TbSysVisitStatistics;
import org.xyzh.common.dto.user.TbSysUser;
import org.xyzh.common.utils.TimeUtils;
import org.xyzh.common.vo.UserDeptRoleVO;
import org.xyzh.common.redis.service.RedisService;
import org.xyzh.system.mapper.SysVisitStatisticsMapper;
import org.xyzh.system.mapper.UserMapper;
import org.xyzh.system.utils.LoginUtil;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
/**
* @description 系统总览控制器
* @filename SystemOverviewController.java
@@ -25,53 +42,193 @@ import java.util.Map;
public class SystemOverviewController {
private static final Logger logger = LoggerFactory.getLogger(SystemOverviewController.class);
@Autowired
private UserMapper userMapper;
@Autowired
private SysVisitStatisticsMapper sysVisitStatisticsMapper;
@Autowired
private ResourceService resourceService;
@Autowired
private RedisService redisService;
@Autowired
private CourseService courseService;
@Autowired
private LearningTaskService learningTaskService;
/**
* 获取系统总览数据统计
*/
@GetMapping("/statistics")
public ResultDomain<Map<String, Object>> getSystemStatistics() {
// TODO: 后续接入真实统计数据(用户表、资源表、访问统计表等)
ResultDomain<Map<String, Object>> result = new ResultDomain<>();
Map<String, Object> data = new HashMap<>();
List<UserDeptRoleVO> voList = LoginUtil.getCurrentDeptRole();
// 顶部统计卡片总用户数、总资源数、今日PV、今日UV
// 1. 总用户数:直接统计用户表数量(后续可增加状态/逻辑删除过滤)
try {
TbSysUser filterUser = new TbSysUser();
// 顶部统计卡片:总用户数、总资源数、今日访问量、活跃用户
data.put("totalUsers", 1234);
data.put("totalUsersChange", "+12%");
long totalUsers = userMapper.countDeptUser(voList.get(0).getDeptID(), filterUser);
data.put("totalUsers", totalUsers);
filterUser.setEndTime(TimeUtils.getEndTimeOfYesterday());
long yesterdayUsers = userMapper.countDeptUser(voList.get(0).getDeptID(), filterUser);
// 目前未记录按天的用户总量,这里先返回"+0%",后续可根据用户注册表统计
data.put("totalUsersChange", formatPercentChange(totalUsers, yesterdayUsers));
data.put("totalResources", 5678);
data.put("totalResourcesChange", "+8%");
// 2. 总资源数:当前总量 & 与昨日总量对比
long totalResources = 0L;
long yesterdayTotalResources = 0L;
data.put("todayVisits", 892);
data.put("todayVisitsChange", "+15%");
// 今天总资源数(不带时间过滤)
ResultDomain<Integer> totalResResult = resourceService.getResourceCount(new TbResource());
if (totalResResult.isSuccess() && totalResResult.getData() != null) {
totalResources = totalResResult.getData();
}
data.put("activeUsers", 456);
data.put("activeUsersChange", "+5%");
// 昨日结束时的资源总数publish_time <= 昨日结束时间
TbResource yesterdayFilter = new TbResource();
yesterdayFilter.setStartTime(TimeUtils.getStartTimeOfYesterday());
yesterdayFilter.setEndTime(TimeUtils.getEndTimeOfYesterday());
ResultDomain<Integer> yesterdayResResult = resourceService.getResourceCount(yesterdayFilter);
if (yesterdayResResult.isSuccess() && yesterdayResResult.getData() != null) {
yesterdayTotalResources = yesterdayResResult.getData();
}
data.put("totalResources", totalResources);
data.put("totalResourcesChange", formatPercentChange(totalResources, yesterdayTotalResources));
} catch (Exception e) {
logger.error("统计总用户数失败", e);
data.put("totalUsers", 0);
}
// 3. 今日访问量 & 活跃用户优先从Redis读取今日数据必要时从系统访问统计表回退
try {
LocalDate todayDate = LocalDate.now();
LocalDate yesterdayDate = todayDate.minusDays(1);
String todayKeySuffix = todayDate.format(java.time.format.DateTimeFormatter.BASIC_ISO_DATE);
String pvKey = "stat:pv:" + todayKeySuffix;
String uvKey = "stat:uv:" + todayKeySuffix;
String yesterdayKeySuffix = yesterdayDate.format(java.time.format.DateTimeFormatter.BASIC_ISO_DATE);
String yesterdayPvKey = "stat:pv:" + yesterdayKeySuffix;
String yesterdayUvKey = "stat:uv:" + yesterdayKeySuffix;
long todayPv = 0L;
long todayUv = 0L;
long yesterdayPv = 0L;
long yesterdayUv = 0L;
try {
Object pvObj = redisService.get(pvKey);
if (pvObj instanceof Number) {
todayPv = ((Number) pvObj).longValue();
}
} catch (Exception e) {
logger.warn("从Redis读取PV失败: {}", e.getMessage());
}
try {
todayUv = redisService.sCard(uvKey);
} catch (Exception e) {
logger.warn("从Redis读取UV失败: {}", e.getMessage());
}
try {
Object yesterdayPvObj = redisService.get(yesterdayPvKey);
if (yesterdayPvObj instanceof Number) {
yesterdayPv = ((Number) yesterdayPvObj).longValue();
}
} catch (Exception e) {
logger.warn("从Redis读取昨日PV失败: {}", e.getMessage());
}
try {
yesterdayUv = redisService.sCard(yesterdayUvKey);
} catch (Exception e) {
logger.warn("从Redis读取昨日UV失败: {}", e.getMessage());
}
// 从统计表中获取活跃用户,作为顶部卡片展示
// 顶部卡片今日PV / 今日UV
data.put("totalPv", todayPv);
data.put("totalPvChange", formatPercentChange(todayPv, yesterdayPv));
data.put("totalUv", todayUv);
data.put("totalUvChange", formatPercentChange(todayUv, yesterdayUv));
} catch (Exception e) {
logger.error("统计访问量/活跃用户失败", e);
data.put("totalPv", 0L);
data.put("totalPvChange", "+0%");
data.put("totalUv", 0L);
data.put("totalUvChange", "+0%");
}
result.success("获取系统总览统计成功", data);
return result;
}
/**
* 获取活跃用户图表数据
* 获取活跃用户图表数据PV和UV
*/
@GetMapping("/active-users")
public ResultDomain<Map<String, Object>> getActiveUsersChart(
@RequestParam(required = true, name = "start") String start, @RequestParam(required = true, name = "end") String end) {
// TODO: 后续根据days参数7/30天查询真实活跃用户统计
ResultDomain<Map<String, Object>> result = new ResultDomain<>();
Map<String, Object> data = new HashMap<>();
// 默认展示7天
LocalDate startDate = LocalDate.parse(start);
LocalDate endDate = LocalDate.parse(end);
long days = startDate.until(endDate).getDays();
LocalDate today = LocalDate.now();
// X轴周一 ~ 周日(示例)
data.put("labels", List.of("周一", "周二", "周三", "周四", "周五", "周六", "周日"));
// Y轴数据活跃用户数量示例数据
data.put("values", List.of(120, 200, 150, 80, 70, 110, 130));
try {
// 按天遍历统计区间内的PV和UV数据
List<String> labels = new ArrayList<>();
List<Integer> pvValues = new ArrayList<>();
List<Integer> uvValues = new ArrayList<>();
result.success("获取活跃用户图表数据成功", data);
LocalDate cursor = startDate;
while (!cursor.isAfter(endDate)) {
labels.add(formatDayLabel(cursor));
int pv = 0;
int uv = 0;
try {
String keySuffix = cursor.format(java.time.format.DateTimeFormatter.BASIC_ISO_DATE);
String pvKey = "stat:pv:" + keySuffix;
String uvKey = "stat:uv:" + keySuffix;
Object pvObj = redisService.get(pvKey);
if (pvObj instanceof Number) {
pv = ((Number) pvObj).intValue();
}
uv = (int) redisService.sCard(uvKey);
} catch (Exception e) {
logger.warn("从Redis读取今日PV/UV失败尝试从数据库读取: {}", e.getMessage());
}
pvValues.add(pv);
uvValues.add(uv);
cursor = cursor.plusDays(1);
}
data.put("labels", labels);
data.put("pvValues", pvValues);
data.put("uvValues", uvValues);
} catch (Exception e) {
logger.error("获取PV/UV图表数据失败", e);
data.put("labels", List.of());
data.put("pvValues", List.of());
data.put("uvValues", List.of());
}
result.success("获取PV/UV图表数据成功", data);
return result;
}
@@ -80,21 +237,36 @@ public class SystemOverviewController {
*/
@GetMapping("/resource-category-stats")
public ResultDomain<Map<String, Object>> getResourceCategoryStats() {
// TODO: 后续从资源表统计各类型资源数量
ResultDomain<Map<String, Object>> result = new ResultDomain<>();
Map<String, Object> data = new HashMap<>();
try {
Map<String, Object> data = new HashMap<>();
// 饼图数据:名称 + 数量
List<Map<String, Object>> categories = List.of(
createCategory("文章", 1048),
createCategory("视频", 735),
createCategory("音频", 580),
createCategory("课程", 484),
createCategory("其他", 300)
);
// 获取各类型资源数量
ResultDomain<Integer> resourceCountResult = resourceService.getResourceCount(new TbResource());
ResultDomain<Integer> courseCountResult = courseService.getCourseCount(new TbCourse());
ResultDomain<Integer> taskCountResult = learningTaskService.getLearningTaskCount(new TbLearningTask());
data.put("items", categories);
result.success("获取资源分类统计成功", data);
// 获取数量失败时默认为0
int resourceCount = (resourceCountResult.isSuccess() && resourceCountResult.getData() != null)
? resourceCountResult.getData() : 0;
int courseCount = (courseCountResult.isSuccess() && courseCountResult.getData() != null)
? courseCountResult.getData() : 0;
int taskCount = (taskCountResult.isSuccess() && taskCountResult.getData() != null)
? taskCountResult.getData() : 0;
// 饼图数据:名称 + 数量
List<Map<String, Object>> categories = List.of(
createCategory("文章", resourceCount),
createCategory("课程", courseCount),
createCategory("学习任务", taskCount)
);
data.put("items", categories);
result.success("获取资源分类统计成功", data);
} catch (Exception e) {
logger.error("获取资源分类统计失败", e);
result.fail("获取资源分类统计失败: " + e.getMessage());
}
return result;
}
@@ -103,16 +275,76 @@ public class SystemOverviewController {
*/
@GetMapping("/today-visits")
public ResultDomain<Map<String, Object>> getTodayVisits() {
// TODO: 后续接入访问日志/统计表,计算今日指标
ResultDomain<Map<String, Object>> result = new ResultDomain<>();
Map<String, Object> data = new HashMap<>();
try {
String todayKeySuffix = LocalDate.now().format(java.time.format.DateTimeFormatter.BASIC_ISO_DATE);
String pvKey = "stat:pv:" + todayKeySuffix;
String uvKey = "stat:uv:" + todayKeySuffix;
data.put("uv", 892);
data.put("pv", 3456);
data.put("avgVisitDuration", "5分32秒");
data.put("bounceRate", "35.6%");
long uvFromRedis = 0L;
long pvFromRedis = 0L;
result.success("获取今日访问量统计成功", data);
try {
pvFromRedis = 0L;
Object pvObj = redisService.get(pvKey);
if (pvObj instanceof Number) {
pvFromRedis = ((Number) pvObj).longValue();
}
} catch (Exception e) {
logger.warn("从Redis读取PV失败: {}", e.getMessage());
}
try {
uvFromRedis = redisService.sCard(uvKey);
} catch (Exception e) {
logger.warn("从Redis读取UV失败: {}", e.getMessage());
}
TbSysVisitStatistics filter = new TbSysVisitStatistics();
filter.setStatDate(new Date());
List<TbSysVisitStatistics> list = sysVisitStatisticsMapper.selectSysVisitStatistics(filter);
TbSysVisitStatistics todayStat = (list != null && !list.isEmpty()) ? list.get(0) : null;
int uvFromDb = todayStat != null && todayStat.getUniqueVisitors() != null ? todayStat.getUniqueVisitors() : 0;
int pvFromDb = todayStat != null && todayStat.getPageViews() != null ? todayStat.getPageViews() : 0;
int avgDurationSec = todayStat != null && todayStat.getAvgVisitDuration() != null ? todayStat.getAvgVisitDuration() : 0;
// 优先使用Redis中的实时数据若没有则回退到统计表
data.put("uv", uvFromRedis > 0 ? uvFromRedis : uvFromDb);
data.put("pv", pvFromRedis > 0 ? pvFromRedis : pvFromDb);
data.put("avgVisitDuration", formatDuration(avgDurationSec));
// 当前表中没有跳出率字段,这里先返回"-",后续有字段后再计算
data.put("bounceRate", "-");
result.success("获取今日访问量统计成功", data);
return result;
} catch (Exception e) {
logger.error("获取今日访问量统计失败", e);
data.put("uv", 0);
data.put("pv", 0);
data.put("avgVisitDuration", "0秒");
data.put("bounceRate", "-");
result.success("获取今日访问量统计成功", data);
return result;
}
}
/**
* 记录页面访问PV由前端在进入系统总览页面时主动调用
*/
@GetMapping("/track-visit")
public ResultDomain<String> trackVisit() {
ResultDomain<String> result = new ResultDomain<>();
try {
String todayKeySuffix = LocalDate.now().format(java.time.format.DateTimeFormatter.BASIC_ISO_DATE);
String pvKey = "stat:pv:" + todayKeySuffix;
redisService.incr(pvKey, 1L);
result.success("记录访问成功", "OK");
} catch (Exception e) {
logger.error("记录PV失败", e);
result.fail("记录访问失败: " + e.getMessage());
}
return result;
}
@@ -121,7 +353,6 @@ public class SystemOverviewController {
*/
@GetMapping("/system-status")
public ResultDomain<Map<String, Object>> getSystemStatus() {
// TODO: 后续接入真实系统运行状态CPU、内存、服务可用性等
ResultDomain<Map<String, Object>> result = new ResultDomain<>();
Map<String, Object> data = new HashMap<>();
@@ -138,4 +369,36 @@ public class SystemOverviewController {
item.put("value", value);
return item;
}
private String formatDayLabel(LocalDate date) {
return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}
private String formatDuration(int seconds) {
if (seconds <= 0) {
return "0秒";
}
int minutes = seconds / 60;
int remain = seconds % 60;
if (minutes == 0) {
return remain + "";
}
return minutes + "" + remain + "";
}
/**
* 计算相对昨日的变化百分比,返回形如"+12%"或"-5%"当昨日为0或无数据时返回"+0%"
*/
private String formatPercentChange(long today, long yesterday) {
if (yesterday <= 0) {
return "+"+today+"%";
}
long diff = today - yesterday;
double percent = diff * 100.0 / yesterday;
// 保留一位小数
String formatted = String.format("%.1f", Math.abs(percent));
String sign = percent >= 0 ? "+" : "-";
return sign + formatted + "%";
}
}

View File

@@ -192,7 +192,7 @@ public interface UserMapper extends BaseMapper<TbSysUser> {
UserVO selectUserInfoTotal(@Param("userId") String userId);
int countDeptUser(@Param("deptId") String deptId);
int countDeptUser(@Param("deptId") String deptId, @Param("filter") TbSysUser filter);
/**
* @description 查询部门及其子部门的所有用户ID

View File

@@ -282,7 +282,7 @@ public class SysUserServiceImpl implements SysUserService {
List<TbSysUser> users = userMapper.selectUserPage(filter, pageParam, userDeptRoles);
PageDomain<TbSysUser> pageDomain = new PageDomain<>();
int count = userMapper.countDeptUser(userDeptRoles.get(0).getDeptID());
int count = userMapper.countDeptUser(userDeptRoles.get(0).getDeptID(), filter);
pageDomain.setDataList(users);
pageDomain.setPageParam(pageParam);
pageParam.setTotalElements(count);
@@ -299,7 +299,7 @@ public class SysUserServiceImpl implements SysUserService {
List<UserVO> userVOs = userMapper.selectUserVOPage(filter, pageParam, userDeptRoles);
PageDomain<UserVO> pageDomain = new PageDomain<>();
int count = userMapper.countDeptUser(userDeptRoles.get(0).getDeptID());
int count = userMapper.countDeptUser(userDeptRoles.get(0).getDeptID(), filter);
pageDomain.setDataList(userVOs);
pageDomain.setPageParam(pageParam);
pageParam.setTotalElements(count);

View File

@@ -95,24 +95,30 @@
<sql id="Filter_Clause">
<where>
deleted = 0
u.deleted = 0
<if test="filter.id != null and filter.id != ''">
AND id = #{filter.id}
AND u.id = #{filter.id}
</if>
<if test="filter.username != null and filter.username != ''">
AND username = #{filter.username}
AND u.username = #{filter.username}
</if>
<if test="filter.email != null and filter.email != ''">
AND email = #{filter.email}
AND u.email = #{filter.email}
</if>
<if test="filter.phone != null and filter.phone != ''">
AND phone = #{filter.phone}
AND u.phone = #{filter.phone}
</if>
<if test="filter.status != null">
AND status = #{filter.status}
AND u.status = #{filter.status}
</if>
<if test="filter.wechatID != null and filter.wechatID != ''">
AND wechat_id = #{filter.wechatID}
AND u.wechat_id = #{filter.wechatID}
</if>
<if test="filter.startTime != null">
AND u.create_time &gt;= #{filter.startTime}
</if>
<if test="filter.endTime != null">
AND u.create_time &lt; #{filter.endTime}
</if>
</where>
</sql>
@@ -575,7 +581,7 @@
FROM tb_sys_user_dept_role tudr
INNER JOIN tb_sys_dept d ON tudr.dept_id = d.dept_id AND d.deleted = 0
INNER JOIN tb_sys_user u ON tudr.user_id = u.id AND u.deleted = 0
WHERE tudr.deleted = 0
<include refid="Filter_Clause"/>
AND d.dept_path LIKE CONCAT(
(SELECT dept_path FROM tb_sys_dept WHERE dept_id = #{deptId} AND deleted = 0),
'%'