页面样式,svg
This commit is contained in:
@@ -169,7 +169,7 @@ INSERT INTO `tb_sys_menu` VALUES
|
||||
('8002', 'menu_admin_crontab_log', '执行日志', 'menu_admin_crontab_manage', '/admin/manage/crontab/log', 'admin/manage/crontab/LogManagementView', NULL, 2, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
|
||||
('8003', 'menu_admin_news_crawler', '新闻爬虫配置', 'menu_admin_crontab_manage', '/admin/manage/crontab/news-crawler', 'admin/manage/crontab/NewsCrawlerView', NULL, 3, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
|
||||
-- 消息通知模块菜单 (9000-9999)
|
||||
('9001', 'menu_admin_message_manage', '消息管理', NULL, '/admin/manage/message', 'admin/manage/message/MessageManageView', 'admin/message.svg', 9, 0, 'SidebarLayout', '1', NULL, '2025-11-13 10:00:00', '2025-11-13 10:00:00', NULL, 0),
|
||||
('9001', 'menu_admin_message_manage', '消息管理', NULL, '/admin/manage/message', 'admin/manage/message/MessageManageView', 'admin/notice.svg', 9, 0, 'SidebarLayout', '1', NULL, '2025-11-13 10:00:00', '2025-11-13 10:00:00', NULL, 0),
|
||||
-- 用户端消息中心菜单 (650-699)
|
||||
('650', 'menu_user_message_center', '消息中心', NULL, '/user/message', 'user/message/MyMessageListView', NULL, 7, 1, 'NavigationLayout', '1', NULL, '2025-11-13 10:00:00', '2025-11-13 10:00:00', NULL, 0),
|
||||
('651', 'menu_user_message_detail', '消息详情', 'menu_user_message_center', '/user/message/detail/:messageID', 'user/message/MyMessageDetailView', NULL, 1, 3, 'NavigationLayout', '1', NULL, '2025-11-13 10:00:00', '2025-11-13 10:00:00', NULL, 0);
|
||||
|
||||
@@ -25,12 +25,12 @@ public interface LoginService {
|
||||
|
||||
/**
|
||||
* @description 退出登录
|
||||
* @param loginDomain 登录域
|
||||
* @param request HttpServletRequest
|
||||
* @return ResultDomain<String> 返回结果
|
||||
* @author yslg
|
||||
* @since 2025-09-28
|
||||
*/
|
||||
ResultDomain<String> logout(LoginDomain loginDomain);
|
||||
ResultDomain<String> logout(HttpServletRequest request);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -70,8 +70,8 @@ public class AuthController {
|
||||
* @since 2025-09-28
|
||||
*/
|
||||
@PostMapping("/logout")
|
||||
public ResultDomain<String> logout(@RequestBody LoginDomain loginDomain) {
|
||||
return loginService.logout(loginDomain);
|
||||
public ResultDomain<String> logout(HttpServletRequest request) {
|
||||
return loginService.logout(request);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -72,7 +72,8 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
}
|
||||
|
||||
// 【优化】从Redis缓存中获取LoginDomain,避免每次都查数据库
|
||||
String redisKey = REDIS_LOGIN_PREFIX + userId;
|
||||
// 多设备登录场景下,以token为维度存储和获取会话信息
|
||||
String redisKey = REDIS_LOGIN_PREFIX + token;
|
||||
LoginDomain loginDomain = (LoginDomain) redisService.get(redisKey);
|
||||
|
||||
if (loginDomain != null && loginDomain.getUser() != null) {
|
||||
|
||||
@@ -137,10 +137,11 @@ public class LoginServiceImpl implements LoginService {
|
||||
loginDomain.setToken(jwtTokenUtil.generateToken(loginDomain));
|
||||
|
||||
// 将LoginDomain存储到Redis中,根据rememberMe设置不同的过期时间
|
||||
String redisKey = "login:token:" + user.getID();
|
||||
String token = loginDomain.getToken();
|
||||
String redisKey = "login:token:" + token;
|
||||
long expireTime = loginParam.isRememberMe()
|
||||
? 7 * 24 * 60 * 60 // rememberMe: 7天
|
||||
: 24 * 60 * 60; // 不rememberMe: 1天
|
||||
? 7 * 24 * 60 * 60
|
||||
: 24 * 60 * 60;
|
||||
redisService.set(redisKey, loginDomain, expireTime, TimeUnit.SECONDS);
|
||||
|
||||
// 登录成功后清除失败次数并记录成功日志
|
||||
@@ -160,14 +161,33 @@ public class LoginServiceImpl implements LoginService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<String> logout(LoginDomain loginDomain) {
|
||||
public ResultDomain<String> logout(HttpServletRequest request) {
|
||||
ResultDomain<String> result = new ResultDomain<>();
|
||||
|
||||
|
||||
try {
|
||||
// TODO: 将token加入黑名单或从Redis中删除
|
||||
// 这里可以实现token黑名单机制
|
||||
|
||||
result.success("退出登录成功", (String)null);
|
||||
// 从请求头中获取 Bearer Token
|
||||
String bearerToken = request.getHeader("Authorization");
|
||||
if (!StringUtils.hasText(bearerToken) || !bearerToken.startsWith("Bearer ")) {
|
||||
result.fail("未提供有效的认证信息");
|
||||
return result;
|
||||
}
|
||||
|
||||
String token = bearerToken.substring(7);
|
||||
|
||||
// 解析 token 获取 userId,作为基本校验
|
||||
String userId = jwtTokenUtil.getUserIdFromToken(token);
|
||||
if (!StringUtils.hasText(userId)) {
|
||||
result.fail("无效的令牌");
|
||||
return result;
|
||||
}
|
||||
|
||||
// 删除当前token对应的 Redis 登录信息(多设备登录场景下不影响其他设备)
|
||||
String redisKey = "login:token:" + token;
|
||||
redisService.delete(redisKey);
|
||||
|
||||
// TODO: 如有需要,可在此处增加 token 黑名单机制
|
||||
|
||||
result.success("退出登录成功", (String) null);
|
||||
} catch (Exception e) {
|
||||
result.fail("退出登录失败: " + e.getMessage());
|
||||
}
|
||||
|
||||
@@ -68,16 +68,16 @@ public class HttpLoginArgumentResolver implements HandlerMethodArgumentResolver
|
||||
|
||||
try {
|
||||
// 验证token格式和有效性
|
||||
if (!tokenParser.validateToken(token, tokenParser.getUserIdFromToken(token))) {
|
||||
String userId = tokenParser.getUserIdFromToken(token);
|
||||
if (!tokenParser.validateToken(token, userId)) {
|
||||
if (httpLogin.required()) {
|
||||
throw new IllegalArgumentException(httpLogin.message());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 从Redis中获取LoginDomain
|
||||
String userId = tokenParser.getUserIdFromToken(token);
|
||||
String redisKey = REDIS_LOGIN_PREFIX + userId;
|
||||
// 从Redis中获取LoginDomain(按token维度存储和获取会话信息)
|
||||
String redisKey = REDIS_LOGIN_PREFIX + token;
|
||||
LoginDomain loginDomain = (LoginDomain) redisService.get(redisKey);
|
||||
|
||||
if (loginDomain == null) {
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
<spring-data-redis.version>3.5.4</spring-data-redis.version>
|
||||
|
||||
<!-- excel -->
|
||||
<poi.version>5.2.3</poi.version>
|
||||
<poi.version>5.4.1</poi.version>
|
||||
|
||||
<lombok.version>1.18.40</lombok.version>
|
||||
</properties>
|
||||
|
||||
@@ -2,9 +2,15 @@ package org.xyzh.system.controller;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
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.common.core.domain.ResultDomain;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -24,9 +30,25 @@ public class SystemOverviewController {
|
||||
*/
|
||||
@GetMapping("/statistics")
|
||||
public ResultDomain<Map<String, Object>> getSystemStatistics() {
|
||||
// TODO: 实现获取系统总览数据统计
|
||||
// 统计系统总用户数、资源总数、今日访问量
|
||||
return null;
|
||||
// TODO: 后续接入真实统计数据(用户表、资源表、访问统计表等)
|
||||
ResultDomain<Map<String, Object>> result = new ResultDomain<>();
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
|
||||
// 顶部统计卡片:总用户数、总资源数、今日访问量、活跃用户
|
||||
data.put("totalUsers", 1234);
|
||||
data.put("totalUsersChange", "+12%");
|
||||
|
||||
data.put("totalResources", 5678);
|
||||
data.put("totalResourcesChange", "+8%");
|
||||
|
||||
data.put("todayVisits", 892);
|
||||
data.put("todayVisitsChange", "+15%");
|
||||
|
||||
data.put("activeUsers", 456);
|
||||
data.put("activeUsersChange", "+5%");
|
||||
|
||||
result.success("获取系统总览统计成功", data);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,9 +56,23 @@ public class SystemOverviewController {
|
||||
*/
|
||||
@GetMapping("/active-users")
|
||||
public ResultDomain<Map<String, Object>> getActiveUsersChart(
|
||||
@RequestParam(required = false) Integer days) {
|
||||
// TODO: 实现获取活跃用户图表数据(折线图,展示7/30天活跃用户数)
|
||||
return null;
|
||||
@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();
|
||||
|
||||
// X轴:周一 ~ 周日(示例)
|
||||
data.put("labels", List.of("周一", "周二", "周三", "周四", "周五", "周六", "周日"));
|
||||
// Y轴数据:活跃用户数量(示例数据)
|
||||
data.put("values", List.of(120, 200, 150, 80, 70, 110, 130));
|
||||
|
||||
result.success("获取活跃用户图表数据成功", data);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,17 +80,22 @@ public class SystemOverviewController {
|
||||
*/
|
||||
@GetMapping("/resource-category-stats")
|
||||
public ResultDomain<Map<String, Object>> getResourceCategoryStats() {
|
||||
// TODO: 实现获取资源分类统计(饼图,展示各类型资源占比)
|
||||
return null;
|
||||
}
|
||||
// TODO: 后续从资源表统计各类型资源数量
|
||||
ResultDomain<Map<String, Object>> result = new ResultDomain<>();
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 获取用户活跃度数据
|
||||
*/
|
||||
@GetMapping("/user-activity")
|
||||
public ResultDomain<Map<String, Object>> getUserActivityData() {
|
||||
// TODO: 实现获取用户活跃度数据
|
||||
return null;
|
||||
// 饼图数据:名称 + 数量
|
||||
List<Map<String, Object>> categories = List.of(
|
||||
createCategory("文章", 1048),
|
||||
createCategory("视频", 735),
|
||||
createCategory("音频", 580),
|
||||
createCategory("课程", 484),
|
||||
createCategory("其他", 300)
|
||||
);
|
||||
|
||||
data.put("items", categories);
|
||||
result.success("获取资源分类统计成功", data);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,8 +103,17 @@ public class SystemOverviewController {
|
||||
*/
|
||||
@GetMapping("/today-visits")
|
||||
public ResultDomain<Map<String, Object>> getTodayVisits() {
|
||||
// TODO: 实现获取今日访问量统计
|
||||
return null;
|
||||
// TODO: 后续接入访问日志/统计表,计算今日指标
|
||||
ResultDomain<Map<String, Object>> result = new ResultDomain<>();
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
|
||||
data.put("uv", 892);
|
||||
data.put("pv", 3456);
|
||||
data.put("avgVisitDuration", "5分32秒");
|
||||
data.put("bounceRate", "35.6%");
|
||||
|
||||
result.success("获取今日访问量统计成功", data);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,7 +121,21 @@ public class SystemOverviewController {
|
||||
*/
|
||||
@GetMapping("/system-status")
|
||||
public ResultDomain<Map<String, Object>> getSystemStatus() {
|
||||
// TODO: 实现获取系统运行状态
|
||||
return null;
|
||||
// TODO: 后续接入真实系统运行状态(CPU、内存、服务可用性等)
|
||||
ResultDomain<Map<String, Object>> result = new ResultDomain<>();
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
|
||||
data.put("status", "UP");
|
||||
data.put("message", "系统运行正常");
|
||||
|
||||
result.success("获取系统运行状态成功", data);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, Object> createCategory(String name, int value) {
|
||||
Map<String, Object> item = new HashMap<>();
|
||||
item.put("name", name);
|
||||
item.put("value", value);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ public class LoginUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 从Redis获取LoginDomain
|
||||
String redisKey = REDIS_LOGIN_PREFIX + userId;
|
||||
// 从Redis获取LoginDomain(按token维度存储和获取会话信息)
|
||||
String redisKey = REDIS_LOGIN_PREFIX + token;
|
||||
LoginDomain loginDomain = (LoginDomain) redisService.get(redisKey);
|
||||
|
||||
if (loginDomain != null) {
|
||||
|
||||
Reference in New Issue
Block a user