image-20251228145842031

AI集成

package top.yxqz.springboot.config;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;

import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * ChatClient 配置类
 * 
 * @author 余小小
 */
@Configuration
public class ChatClientConfig {
    public static final Integer MAX_MEMORY_MESSAGE_SIZE = 30;
    
    /**
     * 配置 ChatMemory - 内存存储的会话记忆
     * 
     * @return ChatMemory 内存存储的会话记忆实例
     */
    @Bean
    public ChatMemory chatMemory() {
        return MessageWindowChatMemory.builder()
                .maxMessages(MAX_MEMORY_MESSAGE_SIZE) // 窗口最大消息数目,保留最近30条消息
                .build();
    }


    /**
     * 配置 ChatClient - OpenAI兼容的聊天客户端
     * 
     * Spring AI 会自动扫描带有 @Tool 注解的方法,无需手动注册
     * 
     * @param openAiChatModel OpenAI Chat模型(由Spring AI自动配置)
     * @param chatMemory 会话记忆存储
     * @return ChatClient 实例
     */
    @Bean("open-ai")
    public ChatClient openAIChatClient(
            OpenAiChatModel openAiChatModel, 
            ChatMemory chatMemory) {
        return ChatClient.builder(openAiChatModel)
                .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory()).build())
                .build();
    }
}

定时文件清理

package top.yxqz.springboot.config;

import lombok.extern.slf4j.Slf4j;
import top.yxqz.springboot.service.FileService;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import jakarta.annotation.Resource;

/**
 * 文件清理定时任务
 * @author 余小小
 */
@Slf4j
@Component
public class FileCleanupScheduler {

    @Resource
    private FileService sysFileInfoService;

    /**
     * 清理过期临时文件
     * 每天凌晨3点执行
     */
    @Scheduled(cron = "0 0 3 * * ?")
    public void cleanupExpiredTempFiles() {
        try {
            log.info("开始执行定时清理过期临时文件任务");
            
            int cleanupCount = sysFileInfoService.cleanupExpiredTempFiles();
            
            log.info("定时清理过期临时文件任务完成,清理数量: {}", cleanupCount);
            
        } catch (Exception e) {
            log.error("定时清理过期临时文件任务执行失败", e);
        }
    }

    /**
     * 文件存储监控
     * 每天上午8点执行
     */
    @Scheduled(cron = "0 0 8 * * ?")
    public void monitorFileStorage() {
        try {
            log.info("开始执行文件存储监控任务");
            
            // TODO: 实现文件存储监控逻辑
            // 1. 统计文件总数
            // 2. 统计存储空间使用情况
            // 3. 检查孤立文件
            // 4. 生成监控报告
            
            log.info("文件存储监控任务完成");
            
        } catch (Exception e) {
            log.error("文件存储监控任务执行失败", e);
        }
    }
} 

JWT(后期引入)

package top.yxqz.springboot.config;

import com.auth0.jwt.exceptions.JWTVerificationException;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import top.yxqz.springboot.dto.response.UserDetailResponseDTO;
import top.yxqz.springboot.enums.UserStatus;
import top.yxqz.springboot.service.UserService;
import top.yxqz.springboot.util.JwtTokenUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

/**
 * JWT认证过滤器
 * 1. 继承OncePerRequestFilter确保每个请求只执行一次
 * 2. 集成Spring Security认证机制
 * 3. 统一的token验证和用户上下文设置
 * 4. 完善的异常处理和日志记录
 * 5. 标准的用户认证系统,支持角色权限
 *
 * @author 余小小
 * @date 2025-01-13
 */
@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Resource
    private UserService userService;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {

        String requestUri = request.getRequestURI();
        String method = request.getMethod();
        log.debug("JWT认证过滤器处理请求:{} {}", method, requestUri);

        try {
            // 1. 提取JWT token
            String token = extractToken(request);

            if (StringUtils.hasText(token)) {
                log.debug("成功提取token,长度:{},前20字符:{}",
                        token.length(),
                        token.length() > 20 ? token.substring(0, 20) + "..." : token);

                // 2. 验证token并获取用户ID
                Long userId = getUserIdFromToken(token);

                if (userId != null) {
                    // 检查token是否过期
                    if (JwtTokenUtils.isTokenExpired(token)) {
                        log.warn("JWT token已过期,用户ID:{}", userId);
                        SecurityContextHolder.clearContext();
                        filterChain.doFilter(request, response);
                        return;
                    }

                    // 3. 查询用户信息(验证用户是否仍然存在和有效)
                    UserDetailResponseDTO user = userService.getUserById(userId);

                    if (user != null && user.getStatus().equals(UserStatus.NORMAL.getCode())) {
                        // 4. 创建Spring Security认证对象
                        List<SimpleGrantedAuthority> authorities = Collections.singletonList(
                                new SimpleGrantedAuthority("ROLE_" + user.getUserType())
                        );

                        UsernamePasswordAuthenticationToken authentication =
                                new UsernamePasswordAuthenticationToken(
                                        user.getUsername(),
                                        null,
                                        authorities
                                );

                        // 5. 设置认证信息到Spring Security上下文
                        SecurityContextHolder.getContext().setAuthentication(authentication);

                        // 6. 设置用户信息到请求属性(方便Controller使用)
                        request.setAttribute("currentUser", user);
                        request.setAttribute("currentUserId", userId);

                        log.debug("JWT认证成功,用户ID:{},用户名:{},用户类型:{}", 
                                userId, user.getUsername(), user.getUserType());
                    } else {
                        log.warn("JWT验证失败:用户不存在或已被禁用,用户ID:{}", userId);
                        // 清理认证上下文,防止安全问题
                        SecurityContextHolder.clearContext();
                    }
                } else {
                    log.warn("JWT验证失败:无法从token中解析用户ID");
                    // 清理认证上下文
                    SecurityContextHolder.clearContext();
                }
            } else {
                log.debug("未找到token,跳过JWT认证");
            }
        } catch (JWTVerificationException e) {
            log.warn("JWT验证失败:{},清理认证上下文", e.getMessage());
            // JWT验证失败时清理认证上下文
            SecurityContextHolder.clearContext();
        } catch (Exception e) {
            log.error("JWT认证过程中发生异常,请求:{} {},异常:{},清理认证上下文", method, requestUri, e.getMessage(), e);
            // 发生异常时清理认证上下文,确保安全
            SecurityContextHolder.clearContext();
        }

        // 继续过滤器链
        filterChain.doFilter(request, response);
    }

    /**
     * 从请求中提取JWT token
     * 只支持标准的 Authorization: Bearer <token> 方式
     *
     * @param request HTTP请求对象
     * @return JWT token字符串,如果没有找到则返回null
     */
    private String extractToken(HttpServletRequest request) {
        String authHeader = request.getHeader("Authorization");
        if (org.springframework.util.StringUtils.hasText(authHeader) && authHeader.startsWith("Bearer ")) {
            return authHeader.substring(7);
        }
        return null;
    }

    /**
     * 从token中获取用户ID
     * @param token JWT token
     * @return 用户ID
     */
    private Long getUserIdFromToken(String token) {
        try {
            return JwtTokenUtils.verifyToken(token).getClaim("userId").asLong();
        } catch (Exception e) {
            log.error("从token获取用户ID失败", e);
            return null;
        }
    }
} 

Jwt配置(后期引入)

package top.yxqz.springboot.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * JWT配置类 - 管理JWT相关的配置属性
 * 
 * 配置项:
 * - jwt.secret: JWT签名密钥
 * - jwt.expiration: JWT过期时间(毫秒)
 * - jwt.refresh-expiration: JWT刷新token过期时间(毫秒)
 * 
 * 使用方式:
 * 在application.properties中配置相应的属性值
 */
@Configuration
@ConfigurationProperties(prefix = "jwt")
public class JwtConfig {
    
    /**
     * JWT签名密钥
     * 默认值:应在配置文件中设置复杂的密钥
     */
    private String secret = "defaultSecretKey";
    
    /**
     * JWT过期时间(毫秒)
     * 默认值:24小时 = 24 * 60 * 60 * 1000 = 86400000
     */
    private Long expiration = 86400000L;
    
    /**
     * JWT刷新token过期时间(毫秒)
     * 默认值:7天 = 7 * 24 * 60 * 60 * 1000 = 604800000
     */
    private Long refreshExpiration = 604800000L;
    
    /**
     * token头部名称
     * 默认值:Authorization
     */
    private String header = "Authorization";
    
    /**
     * token前缀
     * 默认值:Bearer 
     */
    private String tokenPrefix = "Bearer ";
    
    // Getter and Setter methods
    
    public String getSecret() {
        return secret;
    }
    
    public void setSecret(String secret) {
        this.secret = secret;
    }
    
    public Long getExpiration() {
        return expiration;
    }
    
    public void setExpiration(Long expiration) {
        this.expiration = expiration;
    }
    
    public Long getRefreshExpiration() {
        return refreshExpiration;
    }
    
    public void setRefreshExpiration(Long refreshExpiration) {
        this.refreshExpiration = refreshExpiration;
    }
    
    public String getHeader() {
        return header;
    }
    
    public void setHeader(String header) {
        this.header = header;
    }
    
    public String getTokenPrefix() {
        return tokenPrefix;
    }
    
    public void setTokenPrefix(String tokenPrefix) {
        this.tokenPrefix = tokenPrefix;
    }
    
    @Override
    public String toString() {
        return "JwtConfig{" +
                "secret='***'" + // 不输出真实密钥
                ", expiration=" + expiration +
                ", refreshExpiration=" + refreshExpiration +
                ", header='" + header + '\'' +
                ", tokenPrefix='" + tokenPrefix + '\'' +
                '}';
    }
} 

安全框架配置(后期引入)

package top.yxqz.springboot.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * Spring Security 企业级配置类
 * 
 * 🎯 核心职责:
 * 1. 统一认证授权管理 - Spring Security统一处理所有安全相关功能
 * 2. JWT过滤器集成 - 自定义JWT认证过滤器集成到Spring Security过滤器链
 * 3. 无状态会话管理 - 适合微服务和分布式架构
 * 4. 方法级安全支持 - 支持@PreAuthorize等注解
 * 5. 一处配置全局生效 - 消除重复配置,统一维护
 * 
 * 🚀 架构优化:
 * - 解决循环依赖问题:通过延迟注入和职责分离
 * - 提高代码可维护性:清晰的依赖关系
 * - 符合Spring最佳实践:避免复杂的Bean依赖关系
 * 
 * @author 余小小
 * @date 2025-01-27
 */
@Configuration
@EnableWebSecurity
@EnableMethodSecurity  // 启用方法级安全,支持@PreAuthorize等注解
public class SecurityConfig {

    /**
     * 定义公开访问路径常量
     * 这些路径不需要JWT验证,可以直接访问
     */
    private static final String[] PUBLIC_PATHS = {
        // 系统基础路径
        "/",
        "/health",
        "/favicon.ico",
        
        // API文档相关
        "/doc.html",
        "/webjars/**",
        "/v3/api-docs/**",
        "/swagger-ui/**",
        "/swagger-resources/**",
        
        // 认证相关接口(必须公开)
        "/api/user/auth",        // 匿名用户认证(注册/登录)
        "/api/user/login",       // 用户登录
        "/api/user/register",    // 用户注册
        "/api/user/forget",      // 忘记密码
        "/api/user/add",         // 用户添加
        "/api/file/**",          // 临时公开
        "/api/**",
        // 公开信息接口
        "/api/user/{id}",        // 用户信息查询(公开)
        
        // 静态资源(与实际目录结构一致)
        "/static/**",           // 项目静态资源统一路径
        "/files/**",            // 文件上传目录访问
        "/*.html",              // 根路径下的HTML文件
        "/file-test.html"       // 文件测试页面
    };

    /**
     * 密码编码器Bean
     * 
     * 🎯 职责分离:
     * - 独立定义,避免与其他Bean形成循环依赖
     * - 使用BCrypt加密算法,安全性高
     * - 全局共享,其他服务可以直接注入使用
     * 
     * @return PasswordEncoder BCrypt密码编码器
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * JWT认证过滤器Bean
     * 
     * 🎯 解决循环依赖:
     * - 通过@Bean方式创建,而不是@Resource注入
     * - Spring容器会自动处理依赖关系
     * - 避免SecurityConfig直接依赖JwtAuthenticationFilter
     * 
     * @return JwtAuthenticationFilter JWT认证过滤器实例
     */
    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() {
        return new JwtAuthenticationFilter();
    }

    /**
     * 配置Spring Security过滤器链
     * 
     * 核心功能:
     * 1. 禁用CSRF - 适合API服务
     * 2. 无状态会话 - 适合JWT认证
     * 3. 路径权限配置 - 公开路径vs受保护路径
     * 4. JWT过滤器集成 - 自定义认证逻辑
     * 
     *  安全策略:
     * - 默认所有请求需要认证
     * - 公开路径允许匿名访问
     * - JWT过滤器在用户名密码认证之前执行
     * 
     * @param http HttpSecurity配置对象
     * @return SecurityFilterChain 安全过滤器链
     * @throws Exception 配置异常
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            // 禁用CSRF保护(API服务通常不需要)
            .csrf(csrf -> csrf.disable())
            
            // 配置会话管理为无状态(适合JWT)
            .sessionManagement(session -> 
                session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            
            // 配置请求授权规则
            .authorizeHttpRequests(auth -> auth
                // 公开路径,允许匿名访问
                .requestMatchers(PUBLIC_PATHS).permitAll()
                // 其他所有请求都需要认证
                .anyRequest().authenticated()
            )
            
            // 添加JWT认证过滤器
            .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

K4J接口配置

package top.yxqz.kindergraten.config;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Knife4j API文档配置类

 * 用于配置Knife4j的API文档展示及相关资源访问
 * Knife4j是一个基于Swagger的API文档增强工具
 */
@Configuration
public class Knife4jConfig {

    /**
     * 配置OpenAPI对象
     * 用于生成API文档的核心配置
     * 包含API文档的基本信息、安全配置等
     *
     * @return OpenAPI 返回配置好的OpenAPI对象
     */
    @Bean
    public OpenAPI openAPI() {
        return new OpenAPI()
                // 配置接口文档基本信息
                .info(this.getApiInfo());
    }

    /**
     * 获取API文档的基本信息配置
     * 配置API文档的标题、描述、版本等元数据信息
     * 支持以下配置项:
     * - 文档标题
     * - 文档描述
     * - 作者信息(当前已注释)
     * - 许可证信息(当前已注释)
     * - 服务条款(当前已注释)
     * - 版本信息
     *
     * @return Info 返回API文档基本信息对象
     */
    private Info getApiInfo() {
        return new Info()
                // 配置文档标题
                .title("智能幼儿园系统")
                // 配置文档描述
                .description("wwww.yxqz.top")
                // 配置作者信息
                .contact(new Contact().name("余小小").url("https://wwww.yxqz.top").email("yxqz@qq.com"))
                // 配置License许可证信息
//                .license(new License().name("Apache 2.0").url("https://www.baidu.cn"))
                // 概述信息
                .summary("智能幼儿园系统(创新班)")
                .termsOfService("https://www.yxqz.top")
                // 配置版本号
                .version("1.0");
    }
}

日期格式化配置

package top.yxqz.springboot.config;

import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.format.DateTimeFormatter;

@Configuration
public class LocalDateTimeConfig {

    private static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
        return builder -> {
            // 序列化配置
            builder.serializers(new LocalDateTimeSerializer(
                DateTimeFormatter.ofPattern(DATE_TIME_PATTERN)));
            
            // 反序列化配置
            builder.deserializers(new LocalDateTimeDeserializer(
                DateTimeFormatter.ofPattern(DATE_TIME_PATTERN)));
        };
    }
} 

MP配置

package top.yxqz.springboot.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.LocalDateTime;

@Configuration
@Slf4j
//@MapperScan("com.jx.agriculturalsys.controller")
public class MybatisPlusConfig {

    /**
     * 分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

    /**
     * 自动填充处理器
     */
    @Bean
    public MetaObjectHandler metaObjectHandler() {
        return new MetaObjectHandler() {
            @Override
            public void insertFill(MetaObject metaObject) {
                log.debug("开始插入填充...");
                // 创建时间和更新时间(通用字段)
                this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
                this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
                
                // User实体的时间字段
                this.strictInsertFill(metaObject, "createdAt", LocalDateTime.class, LocalDateTime.now());
                this.strictInsertFill(metaObject, "updatedAt", LocalDateTime.class, LocalDateTime.now());
            }

            @Override
            public void updateFill(MetaObject metaObject) {
                log.debug("开始更新填充...");
                // 更新时间(通用字段)
                this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
                
                // User实体的更新时间字段
                this.strictUpdateFill(metaObject, "updatedAt", LocalDateTime.class, LocalDateTime.now());
            }
        };
    }
}

redis缓存配置

package top.yxqz.springboot.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Redis配置类
 * 
 * 核心功能:
 * 1. 配置RedisTemplate使用JSON序列化器
 * 2. 解决默认JDK序列化器的兼容性问题
 * 3. 提高缓存数据的可读性和跨语言兼容性
 * 
 * @author 余小小
 * @date 2025-01-27
 */
@Configuration
public class RedisConfig {

    /**
     * 配置RedisTemplate
     * 
     * 序列化策略:
     * - Key: String序列化器(简单直接)
     * - Value: Jackson JSON序列化器(支持复杂对象)
     * - HashKey: String序列化器
     * - HashValue: Jackson JSON序列化器
     * 
     * @param connectionFactory Redis连接工厂
     * @return 配置好的RedisTemplate
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        // 配置ObjectMapper
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        
        // 创建Jackson序列化器(使用新的构造方法)
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(objectMapper, Object.class);

        // 创建String序列化器
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // 设置key和value的序列化器
        template.setKeySerializer(stringRedisSerializer);           // key使用String序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);   // value使用JSON序列化
        
        // 设置hash key和value的序列化器
        template.setHashKeySerializer(stringRedisSerializer);       // hash key使用String序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer); // hash value使用JSON序列化
        
        // 初始化RedisTemplate
        template.afterPropertiesSet();
        
        return template;
    }
} 

Web路由配置

package top.yxqz.springboot.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * Web配置类 - 企业级统一配置
 * 
 * 🎯 统一职责:
 * 1. API前缀配置
 * 2. 静态资源映射配置  
 * 3. API文档资源配置
 * 
 * 🚀 架构优势:
 * - 统一管理所有Web相关配置
 * - 避免多个配置类冲突
 * - 职责清晰,易于维护
 * - 符合单一配置原则
 * 
 * @author 余小小
 * @date 2025-01-27
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {

    /**
     * 配置API路径前缀
     * 
     * 为所有带有@RestController注解的控制器类自动添加"/api"前缀
     * 这样可以将API接口与其他Web资源(如静态资源、文档)区分开
     * 
     *  工作原理:
     * - UserController: /user/* → /api/user/*
     * - FileController: /file/* → /api/file/*  
     * - EmailController: /email/* → /api/email/*
     * 
     * 排除规则:
     * - Swagger/Knife4j相关接口不添加前缀
     * - 通过包名判断排除springfox、swagger、doc相关包
     * 
     * @param configurer 路径匹配配置器
     */
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.addPathPrefix("/api", clazz ->
                clazz.isAnnotationPresent(RestController.class) &&
                        !clazz.getPackage().getName().contains("springfox") &&
                        !clazz.getPackage().getName().contains("swagger") &&
                        !clazz.getPackage().getName().contains("doc")
        );
    }

    /**
     * 统一配置静态资源映射
     * 
     * 🎯 配置目标:
     * 1. 静态资源访问路径
     * 2. 文件上传目录访问
     * 3. API文档相关资源
     * 4. 避免与API路径冲突
     * 
     * 📁 资源映射规则:
     * - /static/** → classpath:/static/ (项目静态资源)
     * - /files/** → file:./files/ (文件上传目录)
     * - /doc.html → Knife4j文档首页
     * - /webjars/** → Maven webjars资源
     * - /swagger-ui/** → Swagger UI资源
     * - /v3/api-docs/** → OpenAPI文档
     * 
     * @param registry 资源处理器注册表
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 1. 静态资源配置 - 项目自定义静态文件
        registry.addResourceHandler("/static/**")
                .addResourceLocations("classpath:/static/")
                .setCachePeriod(3600); // 缓存1小时
        
        // 2. 文件上传目录配置 - 用户上传文件访问
        registry.addResourceHandler("/files/**")
                .addResourceLocations("file:./files/")
                .setCachePeriod(86400); // 缓存24小时
        
        // 2. API文档资源配置 - Knife4j/Swagger相关
        registry.addResourceHandler("doc.html")
                .addResourceLocations("classpath:/META-INF/resources/");
                
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
                
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
                
        registry.addResourceHandler("/swagger-ui/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/swagger-ui/");
                
        registry.addResourceHandler("/v3/api-docs/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
}
最后修改:2025 年 12 月 29 日
如果觉得我的文章对你有用,请随意赞赏