页面样式,svg

This commit is contained in:
2025-11-14 15:29:02 +08:00
parent 46003a646e
commit 6be3cc6abd
27 changed files with 585 additions and 180 deletions

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);
}
/**

View File

@@ -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) {

View File

@@ -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());
}

View File

@@ -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) {

View File

@@ -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>

View File

@@ -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;
}
}

View File

@@ -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) {