feat(context): 新增业务上下文组件并迁移相关功能

- 创建 `hanserwei-spring-boot-starter-biz-context` 模块,封装为starter供其余模块使用
- 迁移 `HeaderUserId2ContextFilter` 和 `LoginUserContextHolder` 到Starter
- 使用 `TransmittableThreadLocal` 替代普通 `ThreadLocal`
- 在 `han-note-auth` 中引入新的上下文组件依赖
- 调整包结构和日志输出格式
- 异步任务中验证上下文传递功能正常工作
This commit is contained in:
Hanserwei
2025-10-02 23:23:47 +08:00
parent d448c524b7
commit 5c406b48f9
11 changed files with 106 additions and 12 deletions

4
.idea/encodings.xml generated
View File

@@ -7,10 +7,14 @@
<file url="file://$PROJECT_DIR$/han-note-gateway/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/han-note-gateway/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-common/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-common/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-common/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-common/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-spring-boot-starter-biz-context/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-spring-boot-starter-biz-context/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-spring-boot-starter-biz-operationlog/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-spring-boot-starter-biz-operationlog/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-spring-boot-starter-biz-operationlog/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-spring-boot-starter-biz-operationlog/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-spring-boot-starter-jackson/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-spring-boot-starter-jackson/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-spring-boot-starter-jackson/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-spring-boot-starter-jackson/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-spring-starter-biz-context/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-spring-starter-biz-context/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/hanserwei-framework/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/hanserwei-framework/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/hanserwei-framework/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/hanserwei-framework/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />

5
.idea/misc.xml generated
View File

@@ -12,6 +12,11 @@
<option value="$PROJECT_DIR$/han-note-auth/pom.xml" /> <option value="$PROJECT_DIR$/han-note-auth/pom.xml" />
</list> </list>
</option> </option>
<option name="ignoredFiles">
<set>
<option value="$PROJECT_DIR$/hanserwei-framework/hanserwei-spring-starter-biz-context/pom.xml" />
</set>
</option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="temurin-21" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="temurin-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />

View File

@@ -93,8 +93,10 @@
<groupId>com.hanserwei</groupId> <groupId>com.hanserwei</groupId>
<artifactId>hanserwei-common</artifactId> <artifactId>hanserwei-common</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.hanserwei</groupId>
<artifactId>hanserwei-spring-boot-starter-biz-context</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@@ -6,6 +6,7 @@ import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.hanserwei.framework.biz.context.holder.LoginUserContextHolder;
import com.hanserwei.framework.common.enums.DeletedEnum; import com.hanserwei.framework.common.enums.DeletedEnum;
import com.hanserwei.framework.common.enums.StatusEnum; import com.hanserwei.framework.common.enums.StatusEnum;
import com.hanserwei.framework.common.exception.ApiException; import com.hanserwei.framework.common.exception.ApiException;
@@ -21,13 +22,14 @@ import com.hanserwei.hannote.auth.domain.mapper.UserDOMapper;
import com.hanserwei.hannote.auth.domain.mapper.UserRoleDOMapper; import com.hanserwei.hannote.auth.domain.mapper.UserRoleDOMapper;
import com.hanserwei.hannote.auth.enums.LoginTypeEnum; import com.hanserwei.hannote.auth.enums.LoginTypeEnum;
import com.hanserwei.hannote.auth.enums.ResponseCodeEnum; import com.hanserwei.hannote.auth.enums.ResponseCodeEnum;
import com.hanserwei.hannote.auth.filter.LoginUserContextHolder;
import com.hanserwei.hannote.auth.model.vo.user.UserLoginReqVO; import com.hanserwei.hannote.auth.model.vo.user.UserLoginReqVO;
import com.hanserwei.hannote.auth.service.UserService; import com.hanserwei.hannote.auth.service.UserService;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate; import org.springframework.transaction.support.TransactionTemplate;
@@ -46,6 +48,8 @@ public class UserServiceImpl extends ServiceImpl<UserDOMapper, UserDO> implement
private final UserRoleDOMapper userRoleDOMapper; private final UserRoleDOMapper userRoleDOMapper;
private final TransactionTemplate transactionTemplate; private final TransactionTemplate transactionTemplate;
private final RoleDOMapper roleDOMapper; private final RoleDOMapper roleDOMapper;
@Resource(name = "authTaskExecutor")
private ThreadPoolTaskExecutor authTaskExecutor;
@Override @Override
public Response<String> loginAndRegister(UserLoginReqVO reqVO) { public Response<String> loginAndRegister(UserLoginReqVO reqVO) {
@@ -147,6 +151,10 @@ public class UserServiceImpl extends ServiceImpl<UserDOMapper, UserDO> implement
@Override @Override
public Response<?> logout() { public Response<?> logout() {
Long userId = LoginUserContextHolder.getUserId(); Long userId = LoginUserContextHolder.getUserId();
authTaskExecutor.submit(() -> {
Long userId2 = LoginUserContextHolder.getUserId();
log.info("==> 异步线程中获取 userId: {}", userId2);
});
StpUtil.logout(userId); StpUtil.logout(userId);
return Response.success(); return Response.success();
} }

View File

@@ -0,0 +1,48 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.hanserwei</groupId>
<artifactId>hanserwei-framework</artifactId>
<version>${revision}</version>
</parent>
<packaging>jar</packaging>
<artifactId>hanserwei-spring-boot-starter-biz-context</artifactId>
<name>${project.artifactId}</name>
<description>业务上下文组件</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.hanserwei</groupId>
<artifactId>hanserwei-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,13 @@
package com.hanserwei.framework.biz.context.config;
import com.hanserwei.framework.biz.context.filter.HeaderUserId2ContextFilter;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
@AutoConfiguration
public class ContextAutoConfiguration {
@Bean
public HeaderUserId2ContextFilter headerUserId2ContextFilter() {
return new HeaderUserId2ContextFilter();
}
}

View File

@@ -1,5 +1,6 @@
package com.hanserwei.hannote.auth.filter; package com.hanserwei.framework.biz.context.filter;
import com.hanserwei.framework.biz.context.holder.LoginUserContextHolder;
import com.hanserwei.framework.common.constant.GlobalConstants; import com.hanserwei.framework.common.constant.GlobalConstants;
import jakarta.servlet.FilterChain; import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
@@ -12,18 +13,17 @@ import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException; import java.io.IOException;
@Component
@Slf4j @Slf4j
public class HeaderUserId2ContextFilter extends OncePerRequestFilter { public class HeaderUserId2ContextFilter extends OncePerRequestFilter {
@Override @Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws ServletException, IOException { FilterChain chain) throws ServletException, IOException {
log.info("=====> HeaderUserId2ContextFilter 开始执行");
// 从请求头中获取用户 ID // 从请求头中获取用户 ID
String userId = request.getHeader(GlobalConstants.USER_ID); String userId = request.getHeader(GlobalConstants.USER_ID);
log.info("## HeaderUserId2ContextFilter, 用户 ID: {}", userId); log.info("=====> HeaderUserId2ContextFilter, 用户 ID: {}", userId);
// 判断请求头中是否存在用户 ID // 判断请求头中是否存在用户 ID
if (StringUtils.isBlank(userId)) { if (StringUtils.isBlank(userId)) {
@@ -33,7 +33,7 @@ public class HeaderUserId2ContextFilter extends OncePerRequestFilter {
} }
// 如果 header 中存在 userId则设置到 ThreadLocal // 如果 header 中存在 userId则设置到 ThreadLocal
log.info("===== 设置 userId 到 ThreadLocal 中, 用户 ID: {}", userId); log.info("=====> 设置 userId 到 ThreadLocal 中, 用户 ID: {}", userId);
LoginUserContextHolder.setUserId(userId); LoginUserContextHolder.setUserId(userId);
try { try {
@@ -41,7 +41,7 @@ public class HeaderUserId2ContextFilter extends OncePerRequestFilter {
} finally { } finally {
// 一定要删除 ThreadLocal 防止内存泄露 // 一定要删除 ThreadLocal 防止内存泄露
LoginUserContextHolder.remove(); LoginUserContextHolder.remove();
log.info("===== 删除 ThreadLocal userId: {}", userId); log.info("=====> 删除 ThreadLocal userId: {}", userId);
} }
} }
} }

View File

@@ -1,5 +1,6 @@
package com.hanserwei.hannote.auth.filter; package com.hanserwei.framework.biz.context.holder;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.hanserwei.framework.common.constant.GlobalConstants; import com.hanserwei.framework.common.constant.GlobalConstants;
import java.util.HashMap; import java.util.HashMap;
@@ -10,7 +11,7 @@ public class LoginUserContextHolder {
// 初始化一个 ThreadLocal 变量 // 初始化一个 ThreadLocal 变量
private static final ThreadLocal<Map<String, Object>> LOGIN_USER_CONTEXT_THREAD_LOCAL private static final ThreadLocal<Map<String, Object>> LOGIN_USER_CONTEXT_THREAD_LOCAL
= ThreadLocal.withInitial(HashMap::new); = TransmittableThreadLocal.withInitial(HashMap::new);
/** /**

View File

@@ -0,0 +1 @@
com.hanserwei.framework.biz.context.config.ContextAutoConfiguration

View File

@@ -9,6 +9,7 @@
<modules> <modules>
<module>hanserwei-common</module> <module>hanserwei-common</module>
<module>hanserwei-spring-boot-starter-biz-operationlog</module> <module>hanserwei-spring-boot-starter-biz-operationlog</module>
<module>hanserwei-spring-boot-starter-biz-context</module>
<module>hanserwei-spring-boot-starter-jackson</module> <module>hanserwei-spring-boot-starter-jackson</module>
</modules> </modules>

11
pom.xml
View File

@@ -41,6 +41,7 @@
<guava.version>33.5.0-jre</guava.version> <guava.version>33.5.0-jre</guava.version>
<hutool.version>5.8.40</hutool.version> <hutool.version>5.8.40</hutool.version>
<commons-lang3.version>3.19.0</commons-lang3.version> <commons-lang3.version>3.19.0</commons-lang3.version>
<transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
@@ -101,6 +102,11 @@
<artifactId>hanserwei-spring-boot-starter-jackson</artifactId> <artifactId>hanserwei-spring-boot-starter-jackson</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<dependency>
<groupId>com.hanserwei</groupId>
<artifactId>hanserwei-spring-boot-starter-biz-context</artifactId>
<version>${revision}</version>
</dependency>
<dependency> <dependency>
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-bom</artifactId> <artifactId>mybatis-plus-bom</artifactId>
@@ -150,6 +156,11 @@
<artifactId>sa-token-reactor-spring-boot3-starter</artifactId> <artifactId>sa-token-reactor-spring-boot3-starter</artifactId>
<version>${sa-token.version}</version> <version>${sa-token.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>${transmittable-thread-local.version}</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>