短链接分组
功能分析
添加短链接分组
修改短链接分组
查询短链接分组集合
删除短链接分组
短链接分组排序
删除、修改、排序分组
新建分组
设计数据库
CREATE TABLE `t_group` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`gid` varchar(32) DEFAULT NULL COMMENT '分组标识',
`name` varchar(64) DEFAULT NULL COMMENT '分组名称',
`username` varchar(256) DEFAULT NULL COMMENT '创建分组用户名',
`sort_order` int(3) DEFAULT NULL COMMENT '分组排序',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
`del_flag` tinyint(1) DEFAULT NULL COMMENT '删除标识 0:未删除 1:已删除',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_unique_username_gid` (`gid`,`username`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
分组实体
package com.hayaizo.shortlink.admin.dao.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* 短链接分组实体
*/
@Data
@TableName("t_group")
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class GroupDO{
/**
* id
*/
private Long id;
/**
* 分组标识
*/
private String gid;
/**
* 分组名称
*/
private String name;
/**
* 创建分组用户名
*/
private String username;
/**
* 分组排序
*/
private Integer sortOrder;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 修改时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
/**
* 删除标识 0:未删除 1:已删除
*/
@TableField(fill = FieldFill.INSERT)
private Integer delFlag;
}
封装用户上下文
用户信息存储
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 用户信息实体
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserInfoDTO {
/**
* 用户 ID
*/
private String userId;
/**
* 用户名
*/
private String username;
/**
* 真实姓名
*/
private String realName;
/**
* 用户 Token
*/
private String token;
}
UserContext
import com.alibaba.ttl.TransmittableThreadLocal;
import java.util.Optional;
/**
* 用户上下文
*/
public final class UserContext {
private static final ThreadLocal<UserInfoDTO> USER_THREAD_LOCAL = new TransmittableThreadLocal<>();
/**
* 设置用户至上下文
*
* @param user 用户详情信息
*/
public static void setUser(UserInfoDTO user) {
USER_THREAD_LOCAL.set(user);
}
/**
* 获取上下文中用户 ID
*
* @return 用户 ID
*/
public static String getUserId() {
UserInfoDTO userInfoDTO = USER_THREAD_LOCAL.get();
return Optional.ofNullable(userInfoDTO).map(UserInfoDTO::getUserId).orElse(null);
}
/**
* 获取上下文中用户名称
*
* @return 用户名称
*/
public static String getUsername() {
UserInfoDTO userInfoDTO = USER_THREAD_LOCAL.get();
return Optional.ofNullable(userInfoDTO).map(UserInfoDTO::getUsername).orElse(null);
}
/**
* 获取上下文中用户真实姓名
*
* @return 用户真实姓名
*/
public static String getRealName() {
UserInfoDTO userInfoDTO = USER_THREAD_LOCAL.get();
return Optional.ofNullable(userInfoDTO).map(UserInfoDTO::getRealName).orElse(null);
}
/**
* 获取上下文中用户 Token
*
* @return 用户 Token
*/
public static String getToken() {
UserInfoDTO userInfoDTO = USER_THREAD_LOCAL.get();
return Optional.ofNullable(userInfoDTO).map(UserInfoDTO::getToken).orElse(null);
}
/**
* 清理用户上下文
*/
public static void removeUser() {
USER_THREAD_LOCAL.remove();
}
}
UserTransmitFilter
package com.hayaizo.shortlink.admin.common.biz.user;
import com.alibaba.fastjson2.JSON;
import com.google.common.collect.Lists;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.io.IOException;
import java.util.List;
import static com.hayaizo.shortlink.admin.common.constant.RedisCacheConstant.USER_LOGIN_KEY;
/**
* 用户信息传输过滤器
*
*/
@RequiredArgsConstructor
public class UserTransmitFilter implements Filter {
private final StringRedisTemplate stringRedisTemplate;
// 定义白名单 URL 列表
private static final List<String> IGNORE_URLS = Lists.newArrayList(
"/api/short-link/admin/v1/user/login",
"/api/short-link/v1/user/refresh"
);
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String requestURI = httpServletRequest.getRequestURI();
// 如果请求的 URI 在白名单中,直接放行请求
if (IGNORE_URLS.contains(requestURI)) {
filterChain.doFilter(servletRequest, servletResponse);
return; // 直接返回,跳过后续用户信息处理
}
// 非白名单请求继续进行用户信息处理
String username = httpServletRequest.getHeader("username");
String token = httpServletRequest.getHeader("token");
// 如果没有username或者token,则直接返回或考虑设置错误响应
if (username == null || token == null) {
filterChain.doFilter(servletRequest, servletResponse); // 如果选择放行的话
return;
}
// 从 Redis 中获取用户信息
Object userInfoJsonStr = stringRedisTemplate.opsForHash().get(USER_LOGIN_KEY + username, token);
if (userInfoJsonStr != null) {
UserInfoDTO userInfoDTO = JSON.parseObject(userInfoJsonStr.toString(), UserInfoDTO.class);
UserContext.setUser(userInfoDTO); // 将用户信息存入上下文
}
try {
filterChain.doFilter(servletRequest, servletResponse); // 继续处理请求
} finally {
UserContext.removeUser(); // 确保请求结束后清除用户上下文
}
}
}
UserConfiguration
package com.hayaizo.shortlink.admin.config;
import com.hayaizo.shortlink.admin.common.biz.user.UserTransmitFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
/**
* 用户配置自动装配
*/
@Configuration
@RequiredArgsConstructor
public class UserConfiguration {
private final StringRedisTemplate stringRedisTemplate;
/**
* 用户信息传递过滤器
*/
@Bean
public FilterRegistrationBean<UserTransmitFilter> globalUserTransmitFilter() {
FilterRegistrationBean<UserTransmitFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new UserTransmitFilter(stringRedisTemplate));
registration.addUrlPatterns("/*");
registration.setOrder(0);
return registration;
}