feat: 初始化音乐项目后台管理系统- 新增后台管理相关页面和功能组件
- 实现管理员评论管理、数据统计等功能 - 添加后台布局和路由管理 -配置数据库和Redis连接 - 实现文件上传和访问路径配置 - 添加Sa-Token安全配置
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
package com.test.musichouduan.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
/**
|
||||
* 跨域配置
|
||||
*/
|
||||
@Configuration
|
||||
public class CorsConfig {
|
||||
|
||||
@Bean
|
||||
public CorsFilter corsFilter() {
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
// 允许指定域名跨域调用
|
||||
config.addAllowedOrigin("http://localhost:5173");
|
||||
config.addAllowedOrigin("http://localhost:5174");
|
||||
config.addAllowedOrigin("https://your-production-domain.com");
|
||||
// 允许跨域发送cookie
|
||||
config.setAllowCredentials(true);
|
||||
// 放行全部原始头信息
|
||||
config.addAllowedHeader("*");
|
||||
// 允许所有请求方法跨域调用
|
||||
config.addAllowedMethod("*");
|
||||
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", config);
|
||||
return new CorsFilter(source);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.test.musichouduan.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* Knife4j 配置类
|
||||
*/
|
||||
@Configuration
|
||||
public class Knife4jConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
// 配置 Knife4j 的静态资源
|
||||
registry.addResourceHandler("doc.html")
|
||||
.addResourceLocations("classpath:/META-INF/resources/")
|
||||
.resourceChain(false); // 使用默认的资源解析器
|
||||
registry.addResourceHandler("/webjars/**")
|
||||
.addResourceLocations("classpath:/META-INF/resources/webjars/")
|
||||
.resourceChain(false); // 使用默认的资源解析器
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.test.musichouduan.config;
|
||||
|
||||
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.GenericJackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
/**
|
||||
* Redis配置
|
||||
*/
|
||||
@Configuration
|
||||
public class RedisConfig {
|
||||
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
|
||||
// 使用StringRedisSerializer来序列化和反序列化redis的key值
|
||||
template.setKeySerializer(new StringRedisSerializer());
|
||||
// 使用GenericJackson2JsonRedisSerializer来序列化和反序列化redis的value值
|
||||
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
|
||||
|
||||
// Hash的key也采用StringRedisSerializer的序列化方式
|
||||
template.setHashKeySerializer(new StringRedisSerializer());
|
||||
// Hash的value也采用GenericJackson2JsonRedisSerializer的序列化方式
|
||||
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
|
||||
|
||||
template.afterPropertiesSet();
|
||||
return template;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.test.musichouduan.config;
|
||||
|
||||
import cn.dev33.satoken.interceptor.SaInterceptor;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Sa-Token配置
|
||||
*/
|
||||
@Configuration
|
||||
public class SaTokenConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 注册Sa-Token拦截器,打开注解式鉴权功能
|
||||
registry.addInterceptor(new SaInterceptor(handle -> {
|
||||
// 打印调试信息
|
||||
System.out.println("\n=== Sa-Token 拦截器检查登录状态 ===\n");
|
||||
|
||||
// 获取当前登录用户ID
|
||||
Object loginId = StpUtil.getLoginIdDefaultNull();
|
||||
System.out.println("当前登录用户ID: " + loginId);
|
||||
|
||||
// 获取当前用户角色
|
||||
List<String> roleList = StpUtil.getRoleList();
|
||||
System.out.println("当前用户角色: " + roleList);
|
||||
|
||||
// 检查登录状态
|
||||
StpUtil.checkLogin();
|
||||
}))
|
||||
.addPathPatterns("/api/**")
|
||||
.excludePathPatterns(
|
||||
"/api/user/login",
|
||||
"/api/user/register",
|
||||
"/api/music/list",
|
||||
"/api/music/latest",
|
||||
"/api/music/hot",
|
||||
"/api/music/*/play",
|
||||
"/api/playlist/list",
|
||||
"/api/playlist/latest",
|
||||
"/api/playlist/hot",
|
||||
"/api/playlist/official",
|
||||
"/api/playlist/*/play",
|
||||
"/api/singer/list",
|
||||
"/api/singer/latest",
|
||||
"/api/type/**",
|
||||
"/api/banner/list",
|
||||
"/api/comment/list"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.test.musichouduan.config;
|
||||
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import com.test.musichouduan.entity.User;
|
||||
import com.test.musichouduan.repository.UserRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 自定义权限验证接口扩展
|
||||
*/
|
||||
@Component
|
||||
public class StpInterfaceImpl implements StpInterface {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
/**
|
||||
* 返回一个账号所拥有的权限码集合
|
||||
*/
|
||||
@Override
|
||||
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||
// 暂时不需要权限码
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个账号所拥有的角色标识集合
|
||||
*/
|
||||
@Override
|
||||
public List<String> getRoleList(Object loginId, String loginType) {
|
||||
List<String> roles = new ArrayList<>();
|
||||
|
||||
// 获取用户ID
|
||||
Long userId = Long.parseLong(loginId.toString());
|
||||
|
||||
// 查询用户
|
||||
userRepository.findById(userId).ifPresent(user -> {
|
||||
// 根据用户的role字段设置角色
|
||||
if (user.getRole() == 1) {
|
||||
roles.add("admin");
|
||||
} else {
|
||||
roles.add("user");
|
||||
}
|
||||
|
||||
// 打印调试信息
|
||||
System.out.println("用户ID: " + userId + ", 角色: " + roles);
|
||||
});
|
||||
|
||||
return roles;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.test.musichouduan.config;
|
||||
|
||||
import io.swagger.v3.oas.models.Components;
|
||||
import io.swagger.v3.oas.models.ExternalDocumentation;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.info.License;
|
||||
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||
import io.swagger.v3.oas.models.servers.Server;
|
||||
import org.springdoc.core.models.GroupedOpenApi;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Knife4j配置
|
||||
*/
|
||||
@Configuration
|
||||
public class SwaggerConfig {
|
||||
|
||||
@Bean
|
||||
public OpenAPI openAPI() {
|
||||
// 添加服务器信息
|
||||
List<Server> servers = new ArrayList<>();
|
||||
servers.add(new Server().url("/").description("本地服务"));
|
||||
|
||||
return new OpenAPI()
|
||||
.info(new Info()
|
||||
.title("音乐应用API文档")
|
||||
.description("音乐应用后端API接口文档")
|
||||
.version("v1.0.0")
|
||||
.contact(new Contact()
|
||||
.name("Admin")
|
||||
.email("admin@example.com")
|
||||
.url("https://example.com"))
|
||||
.license(new License()
|
||||
.name("Apache 2.0")
|
||||
.url("https://www.apache.org/licenses/LICENSE-2.0.html")))
|
||||
.externalDocs(new ExternalDocumentation()
|
||||
.description("项目文档")
|
||||
.url("https://example.com/docs"))
|
||||
.servers(servers)
|
||||
.addSecurityItem(new SecurityRequirement().addList("bearerAuth"))
|
||||
.components(new Components()
|
||||
.addSecuritySchemes("bearerAuth", new SecurityScheme()
|
||||
.name("bearerAuth")
|
||||
.type(SecurityScheme.Type.HTTP)
|
||||
.scheme("bearer")
|
||||
.bearerFormat("JWT")));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi userApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("用户接口")
|
||||
.pathsToMatch("/api/user/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi musicApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("音乐接口")
|
||||
.pathsToMatch("/api/music/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi singerApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("歌手接口")
|
||||
.pathsToMatch("/api/singer/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi playlistApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("歌单接口")
|
||||
.pathsToMatch("/api/playlist/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi commentApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("评论接口")
|
||||
.pathsToMatch("/api/comment/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi typeApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("类型接口")
|
||||
.pathsToMatch("/api/type/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi bannerApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("轮播图接口")
|
||||
.pathsToMatch("/api/banner/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi adminApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("管理员接口")
|
||||
.pathsToMatch("/api/admin/**")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.test.musichouduan.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* Web配置类
|
||||
*/
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
* 跨域配置
|
||||
*/
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedOriginPatterns("*")
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
||||
.allowedHeaders("*")
|
||||
.allowCredentials(true)
|
||||
.maxAge(3600);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.test.musichouduan.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* Web MVC 配置
|
||||
*/
|
||||
@Configuration
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
@Value("${file.upload-path}")
|
||||
private String uploadPath;
|
||||
|
||||
@Value("${file.access-path}")
|
||||
private String accessPath;
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
// 配置上传文件资源映射
|
||||
registry.addResourceHandler(accessPath + "**")
|
||||
.addResourceLocations("file:" + uploadPath);
|
||||
|
||||
// 注意:前后端分离项目,不需要配置静态资源映射
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.test.musichouduan.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
|
||||
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSocketMessageBroker
|
||||
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Override
|
||||
public void configureMessageBroker(MessageBrokerRegistry config) {
|
||||
// 配置消息代理,用于广播式消息(例如,/topic/ 前缀)
|
||||
// 客户端订阅 "/topic/comments/{targetType}/{targetId}" 来接收特定目标的评论更新
|
||||
config.enableSimpleBroker("/topic");
|
||||
// 配置应用程序目标前缀,客户端发送消息到服务器的目标前缀(例如,/app/ 前缀)
|
||||
// 这个项目我们主要用服务器主动推送,所以 /app 可能用不到,但先配置上
|
||||
config.setApplicationDestinationPrefixes("/app");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
// 注册一个 STOMP 端点,客户端将使用它连接到 WebSocket 服务器
|
||||
// "/ws" 是连接的端点路径
|
||||
// withSockJS() 启用 SockJS 后备选项,以便在 WebSocket 不可用时使用其他传输方式
|
||||
// setAllowedOrigins("*") 允许所有来源的跨域连接,在生产环境中应配置为具体的来源
|
||||
registry.addEndpoint("/ws")
|
||||
.setAllowedOrigins("http://localhost:5173", "http://localhost:5174", "https://your-production-domain.com")
|
||||
.withSockJS(); // 启用 SockJS 支持
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user