feat: 初始化音乐项目后台管理系统- 新增后台管理相关页面和功能组件
- 实现管理员评论管理、数据统计等功能 - 添加后台布局和路由管理 -配置数据库和Redis连接 - 实现文件上传和访问路径配置 - 添加Sa-Token安全配置
This commit is contained in:
64
music-houduan/.gitattributes
vendored
Normal file
64
music-houduan/.gitattributes
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
### Example user template template
|
||||
### Example user template
|
||||
|
||||
# IntelliJ project files
|
||||
.idea
|
||||
target
|
||||
*.gz
|
||||
*.log
|
||||
.log
|
||||
*.logs
|
||||
.logs
|
||||
*.zip
|
||||
*.iml
|
||||
out
|
||||
gen
|
||||
*/.vscode
|
||||
.vscode
|
||||
#忽略所有.svn目录
|
||||
.svn/
|
||||
#忽略所有target目录
|
||||
target/
|
||||
#忽略所有.idea目录及其内容
|
||||
.idea/
|
||||
.idea/*
|
||||
#忽略所有.ipr文件
|
||||
*.ipr
|
||||
#忽略所有.iws文件
|
||||
*.iws
|
||||
#忽略所有.gradle目录
|
||||
.gradle/
|
||||
#忽略所有.settings目录
|
||||
.settings/
|
||||
#忽略所有.classpath文件
|
||||
.classpath
|
||||
#忽略所有.project文件
|
||||
.project
|
||||
#忽略所有.pydevproject文件
|
||||
.pydevproject
|
||||
#忽略所有.DS_Store文件
|
||||
.DS_Store
|
||||
#忽略所有.class文件
|
||||
*.class
|
||||
#忽略所有.log文件
|
||||
*.log
|
||||
#忽略所有.tmp文件
|
||||
*.tmp
|
||||
#忽略所有.swp文件
|
||||
*.swp
|
||||
node_modules/
|
||||
/dist/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.git
|
||||
|
||||
# Editor directories and files
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
dist/
|
||||
npm-debug.log
|
||||
.vscode/
|
||||
README.md
|
||||
39
music-houduan/.gitignore
vendored
Normal file
39
music-houduan/.gitignore
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
*.log
|
||||
*.logs
|
||||
*/*.log
|
||||
*/.log
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
logs
|
||||
node_modules
|
||||
145
music-houduan/pom.xml
Normal file
145
music-houduan/pom.xml
Normal file
@@ -0,0 +1,145 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.5</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.test</groupId>
|
||||
<artifactId>music-houduan</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>music-houduan</name>
|
||||
<description>音乐应用后端</description>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<sa-token.version>1.38.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Boot 基础依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 开发工具 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- MySQL Connector -->
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Data JPA -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot Starter Data Redis -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Redis连接池 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 权限认证 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合 Redis -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-redis-jackson</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 参数校验 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<!-- WebSocket -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 文件上传 -->
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
<version>1.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 图片处理 -->
|
||||
<dependency>
|
||||
<groupId>net.coobird</groupId>
|
||||
<artifactId>thumbnailator</artifactId>
|
||||
<version>0.4.20</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Knife4j API文档 -->
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
|
||||
<version>4.5.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.test.musichouduan;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
/**
|
||||
* 音乐应用后端启动类
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableTransactionManagement
|
||||
@EnableScheduling
|
||||
public class MusicHouduanApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MusicHouduanApplication.class, args);
|
||||
System.out.println("\n---------- 音乐应用后端启动成功 ----------\n");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 支持
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package com.test.musichouduan.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import com.test.musichouduan.dto.BannerDTO;
|
||||
import com.test.musichouduan.dto.PageResult;
|
||||
import com.test.musichouduan.dto.Result;
|
||||
import com.test.musichouduan.service.BannerService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 轮播图控制器
|
||||
*/
|
||||
@Tag(name = "轮播图接口", description = "轮播图的增删改查操作")
|
||||
@RestController
|
||||
@RequestMapping("/api/banner")
|
||||
public class BannerController {
|
||||
|
||||
@Autowired
|
||||
private BannerService bannerService;
|
||||
|
||||
/**
|
||||
* 根据ID获取轮播图
|
||||
*
|
||||
* @param id 轮播图ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "根据ID获取轮播图", description = "根据轮播图ID获取轮播图详情")
|
||||
@GetMapping("/{id}")
|
||||
public Result<BannerDTO> getBannerById(@Parameter(description = "轮播图ID") @PathVariable Long id) {
|
||||
BannerDTO bannerDTO = bannerService.getBannerById(id);
|
||||
return Result.success(bannerDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取轮播图列表
|
||||
*
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "获取轮播图列表", description = "获取所有启用状态的轮播图列表")
|
||||
@GetMapping("/list")
|
||||
public Result<List<BannerDTO>> getBannerList() {
|
||||
List<BannerDTO> bannerList = bannerService.getBannerList();
|
||||
return Result.success(bannerList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页获取轮播图列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "分页获取轮播图列表", description = "分页获取轮播图列表,需要管理员权限")
|
||||
@SaCheckRole("admin")
|
||||
@GetMapping("/page")
|
||||
public Result<PageResult<BannerDTO>> getBannerPage(
|
||||
@Parameter(description = "页码", example = "1") @RequestParam(defaultValue = "1") Integer page,
|
||||
@Parameter(description = "每页大小", example = "10") @RequestParam(defaultValue = "10") Integer size) {
|
||||
PageResult<BannerDTO> pageResult = bannerService.getBannerPage(page, size);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加轮播图
|
||||
*
|
||||
* @param bannerDTO 轮播图DTO
|
||||
* @param image 图片文件
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "添加轮播图", description = "添加轮播图,需要管理员权限")
|
||||
@SaCheckRole("admin")
|
||||
@PostMapping("/add")
|
||||
public Result<BannerDTO> addBanner(
|
||||
@Parameter(description = "轮播图信息") @RequestPart("banner") BannerDTO bannerDTO,
|
||||
@Parameter(description = "轮播图图片文件") @RequestPart("image") MultipartFile image) {
|
||||
BannerDTO result = bannerService.addBanner(bannerDTO, image);
|
||||
return Result.success("添加成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新轮播图
|
||||
*
|
||||
* @param id 轮播图ID
|
||||
* @param bannerDTO 轮播图DTO
|
||||
* @param image 图片文件
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "更新轮播图", description = "更新轮播图信息,需要管理员权限")
|
||||
@SaCheckRole("admin")
|
||||
@PutMapping("/{id}")
|
||||
public Result<BannerDTO> updateBanner(
|
||||
@Parameter(description = "轮播图ID") @PathVariable Long id,
|
||||
@Parameter(description = "轮播图信息") @RequestPart("banner") BannerDTO bannerDTO,
|
||||
@Parameter(description = "轮播图图片文件,可选") @RequestPart(value = "image", required = false) MultipartFile image) {
|
||||
BannerDTO result = bannerService.updateBanner(id, bannerDTO, image);
|
||||
return Result.success("更新成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除轮播图
|
||||
*
|
||||
* @param id 轮播图ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "删除轮播图", description = "删除轮播图,需要管理员权限")
|
||||
@SaCheckRole("admin")
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Boolean> deleteBanner(@Parameter(description = "轮播图ID") @PathVariable Long id) {
|
||||
boolean result = bannerService.deleteBanner(id);
|
||||
return Result.success("删除成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新轮播图状态
|
||||
*
|
||||
* @param id 轮播图ID
|
||||
* @param status 状态
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "更新轮播图状态", description = "更新轮播图状态,0-禁用,1-启用,需要管理员权限")
|
||||
@SaCheckRole("admin")
|
||||
@PutMapping("/{id}/status")
|
||||
public Result<Boolean> updateBannerStatus(
|
||||
@Parameter(description = "轮播图ID") @PathVariable Long id,
|
||||
@Parameter(description = "状态,0-禁用,1-启用", example = "1") @RequestParam Integer status) {
|
||||
boolean result = bannerService.updateBannerStatus(id, status);
|
||||
return Result.success("更新成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新轮播图排序
|
||||
*
|
||||
* @param id 轮播图ID
|
||||
* @param orderNum 排序号
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "更新轮播图排序", description = "更新轮播图排序号,数字越小越靠前,需要管理员权限")
|
||||
@SaCheckRole("admin")
|
||||
@PutMapping("/{id}/order")
|
||||
public Result<Boolean> updateBannerOrder(
|
||||
@Parameter(description = "轮播图ID") @PathVariable Long id,
|
||||
@Parameter(description = "排序号,数字越小越靠前", example = "1") @RequestParam Integer orderNum) {
|
||||
boolean result = bannerService.updateBannerOrder(id, orderNum);
|
||||
return Result.success("更新成功", result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.test.musichouduan.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 基本测试控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/basic-test")
|
||||
public class BasicTestController {
|
||||
|
||||
/**
|
||||
* 基本测试接口
|
||||
*/
|
||||
@GetMapping
|
||||
public Map<String, Object> test() {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("code", 200);
|
||||
result.put("message", "基本测试成功");
|
||||
result.put("data", "Hello, Basic Test!");
|
||||
result.put("time", System.currentTimeMillis());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.test.musichouduan.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.test.musichouduan.dto.CommentDTO;
|
||||
import com.test.musichouduan.dto.PageResult;
|
||||
import com.test.musichouduan.dto.Result;
|
||||
import com.test.musichouduan.service.CommentService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 评论控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/comment")
|
||||
public class CommentController {
|
||||
|
||||
@Autowired
|
||||
private CommentService commentService;
|
||||
|
||||
/**
|
||||
* 根据ID获取评论
|
||||
*
|
||||
* @param id 评论ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public Result<CommentDTO> getCommentById(@PathVariable Long id) {
|
||||
CommentDTO commentDTO = commentService.getCommentById(id);
|
||||
return Result.success(commentDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页获取评论列表
|
||||
*
|
||||
* @param targetId 目标ID
|
||||
* @param targetType 目标类型
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public Result<PageResult<CommentDTO>> getCommentList(
|
||||
@RequestParam Long targetId,
|
||||
@RequestParam Integer targetType,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size) {
|
||||
PageResult<CommentDTO> pageResult = commentService.getCommentList(targetId, targetType, page, size);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加评论
|
||||
*
|
||||
* @param commentDTO 评论DTO
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@PostMapping("/add")
|
||||
public Result<CommentDTO> addComment(@RequestBody CommentDTO commentDTO) {
|
||||
CommentDTO result = commentService.addComment(commentDTO);
|
||||
return Result.success("评论成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除评论
|
||||
*
|
||||
* @param id 评论ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Boolean> deleteComment(@PathVariable Long id) {
|
||||
boolean result = commentService.deleteComment(id);
|
||||
return Result.success("删除成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户的评论列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@GetMapping("/user")
|
||||
public Result<PageResult<CommentDTO>> getUserCommentList(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size) {
|
||||
// 获取当前登录用户ID
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
PageResult<CommentDTO> pageResult = commentService.getUserCommentList(userId, page, size);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.test.musichouduan.controller;
|
||||
|
||||
import com.test.musichouduan.dto.Result;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 回退控制器
|
||||
* 处理所有未匹配的请求
|
||||
* 使用Order注解指定优先级,数值越大优先级越低
|
||||
*/
|
||||
@RestController
|
||||
@Order(Integer.MAX_VALUE)
|
||||
public class FallbackController {
|
||||
|
||||
/**
|
||||
* 处理所有非API请求
|
||||
*
|
||||
* @param request HTTP请求
|
||||
* @param response HTTP响应
|
||||
* @return 响应结果
|
||||
*/
|
||||
@RequestMapping({"/1", "/2", "/3", "/4", "/5", "/6", "/7", "/8", "/9"})
|
||||
public Result<String> handleFallback(HttpServletRequest request, HttpServletResponse response) {
|
||||
// 设置HTTP状态码为404
|
||||
response.setStatus(HttpStatus.NOT_FOUND.value());
|
||||
|
||||
// 返回错误信息
|
||||
return Result.error(HttpStatus.NOT_FOUND.value(), "资源不存在: " + request.getRequestURI());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.test.musichouduan.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import com.test.musichouduan.dto.Result;
|
||||
import com.test.musichouduan.service.FileService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 文件控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/file")
|
||||
public class FileController {
|
||||
|
||||
@Autowired
|
||||
private FileService fileService;
|
||||
|
||||
/**
|
||||
* 上传图片
|
||||
*
|
||||
* @param file 图片文件
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@PostMapping("/upload/image")
|
||||
public Result<String> uploadImage(@RequestParam("file") MultipartFile file) {
|
||||
String url = fileService.uploadImage(file);
|
||||
return Result.success("上传成功", url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传音乐
|
||||
*
|
||||
* @param file 音乐文件
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@PostMapping("/upload/music")
|
||||
public Result<String> uploadMusic(@RequestParam("file") MultipartFile file) {
|
||||
String url = fileService.uploadMusic(file);
|
||||
return Result.success("上传成功", url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传歌词
|
||||
*
|
||||
* @param file 歌词文件
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@PostMapping("/upload/lyric")
|
||||
public Result<String> uploadLyric(@RequestParam("file") MultipartFile file) {
|
||||
String lyric = fileService.uploadLyric(file);
|
||||
return Result.success("上传成功", lyric);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*
|
||||
* @param url 文件URL
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@DeleteMapping("/delete")
|
||||
public Result<Boolean> deleteFile(@RequestParam String url) {
|
||||
boolean result = fileService.deleteFile(url);
|
||||
return Result.success("删除成功", result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.test.musichouduan.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 简单的Hello控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
public class HelloController {
|
||||
|
||||
/**
|
||||
* 欢迎接口
|
||||
*/
|
||||
@GetMapping("/hello")
|
||||
public Map<String, Object> hello() {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("code", 200);
|
||||
result.put("message", "Hello, Music API!");
|
||||
result.put("data", "欢迎使用音乐应用API");
|
||||
result.put("timestamp", System.currentTimeMillis());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试热部署接口
|
||||
*/
|
||||
@GetMapping("/test")
|
||||
public Map<String, Object> test() {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("code", 200);
|
||||
result.put("message", "测试成功");
|
||||
result.put("data", "这是一个测试接口,可以修改此处来测试热部署");
|
||||
result.put("timestamp", System.currentTimeMillis());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
package com.test.musichouduan.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.test.musichouduan.dto.MusicDTO;
|
||||
import com.test.musichouduan.dto.PageResult;
|
||||
import com.test.musichouduan.dto.Result;
|
||||
import com.test.musichouduan.service.MusicService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 音乐控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/music")
|
||||
public class MusicController {
|
||||
|
||||
@Autowired
|
||||
private MusicService musicService;
|
||||
|
||||
/**
|
||||
* 根据ID获取音乐
|
||||
*
|
||||
* @param id 音乐ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public Result<MusicDTO> getMusicById(@PathVariable Long id) {
|
||||
MusicDTO musicDTO = musicService.getMusicById(id);
|
||||
if (musicDTO == null) {
|
||||
// 如果音乐不存在,返回空数据而不是错误
|
||||
return Result.success(null);
|
||||
}
|
||||
return Result.success(musicDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页获取音乐列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @param keyword 关键字
|
||||
* @param typeId 类型ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public Result<PageResult<MusicDTO>> getMusicList(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) Long typeId) {
|
||||
PageResult<MusicDTO> pageResult = musicService.getMusicList(page, size, keyword, typeId);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据歌手ID获取音乐列表
|
||||
*
|
||||
* @param singerId 歌手ID
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/singer/{singerId}")
|
||||
public Result<PageResult<MusicDTO>> getMusicListBySinger(
|
||||
@PathVariable Long singerId,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size) {
|
||||
PageResult<MusicDTO> pageResult = musicService.getMusicListBySinger(singerId, page, size);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新音乐列表
|
||||
*
|
||||
* @param limit 限制数量
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/latest")
|
||||
public Result<List<MusicDTO>> getLatestMusic(@RequestParam(defaultValue = "10") Integer limit) {
|
||||
List<MusicDTO> musicList = musicService.getLatestMusic(limit);
|
||||
return Result.success(musicList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取热门音乐列表
|
||||
*
|
||||
* @param limit 限制数量
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/hot")
|
||||
public Result<List<MusicDTO>> getHotMusic(@RequestParam(defaultValue = "10") Integer limit) {
|
||||
List<MusicDTO> musicList = musicService.getHotMusic(limit);
|
||||
return Result.success(musicList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索音乐
|
||||
*
|
||||
* @param keyword 关键字
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/search")
|
||||
public Result<PageResult<MusicDTO>> searchMusic(
|
||||
@RequestParam String keyword,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size) {
|
||||
PageResult<MusicDTO> pageResult = musicService.getMusicList(page, size, keyword, null);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加音乐
|
||||
*
|
||||
* @param musicDTO 音乐DTO
|
||||
* @param file 音乐文件
|
||||
* @param cover 封面文件
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@PostMapping("/add")
|
||||
public Result<MusicDTO> addMusic(
|
||||
@RequestPart("music") MusicDTO musicDTO,
|
||||
@RequestPart(value = "file", required = false) MultipartFile file,
|
||||
@RequestPart(value = "cover", required = false) MultipartFile cover) {
|
||||
MusicDTO result = musicService.addMusic(musicDTO, file, cover);
|
||||
return Result.success("添加成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新音乐
|
||||
*
|
||||
* @param id 音乐ID
|
||||
* @param musicDTO 音乐DTO
|
||||
* @param file 音乐文件
|
||||
* @param cover 封面文件
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@PutMapping("/{id}")
|
||||
public Result<MusicDTO> updateMusic(
|
||||
@PathVariable Long id,
|
||||
@RequestPart("music") MusicDTO musicDTO,
|
||||
@RequestPart(value = "file", required = false) MultipartFile file,
|
||||
@RequestPart(value = "cover", required = false) MultipartFile cover,
|
||||
@RequestPart(value = "lyric", required = false) MultipartFile lyric) {
|
||||
MusicDTO result = musicService.updateMusic(id, musicDTO, file, cover, lyric);
|
||||
return Result.success("更新成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除音乐
|
||||
*
|
||||
* @param id 音乐ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Boolean> deleteMusic(@PathVariable Long id) {
|
||||
boolean result = musicService.deleteMusic(id);
|
||||
return Result.success("删除成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 收藏/取消收藏音乐
|
||||
*
|
||||
* @param id 音乐ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@PostMapping("/{id}/collect")
|
||||
public Result<Boolean> toggleCollectMusic(@PathVariable Long id) {
|
||||
// 获取当前登录用户ID
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
boolean isCollected = musicService.toggleCollectMusic(id, userId);
|
||||
String message = isCollected ? "收藏成功" : "取消收藏成功";
|
||||
return Result.success(message, isCollected);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户收藏的音乐列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@GetMapping("/collect")
|
||||
public Result<PageResult<MusicDTO>> getCollectedMusic(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size) {
|
||||
// 获取当前登录用户ID
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
PageResult<MusicDTO> pageResult = musicService.getCollectedMusic(userId, page, size);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放音乐
|
||||
*
|
||||
* @param id 音乐ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@PostMapping("/{id}/play")
|
||||
public Result<MusicDTO> playMusic(@PathVariable Long id) {
|
||||
MusicDTO musicDTO = musicService.playMusic(id);
|
||||
return Result.success(musicDTO);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
package com.test.musichouduan.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import com.test.musichouduan.dto.PageResult;
|
||||
import com.test.musichouduan.dto.PlaylistDTO;
|
||||
import com.test.musichouduan.dto.Result;
|
||||
import com.test.musichouduan.service.PlaylistService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 歌单控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/playlist")
|
||||
public class PlaylistController {
|
||||
|
||||
@Autowired
|
||||
private PlaylistService playlistService;
|
||||
|
||||
/**
|
||||
* 根据ID获取歌单
|
||||
*
|
||||
* @param id 歌单ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public Result<PlaylistDTO> getPlaylistById(@PathVariable Long id) {
|
||||
PlaylistDTO playlistDTO = playlistService.getPlaylistById(id);
|
||||
return Result.success(playlistDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页获取歌单列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @param keyword 关键字
|
||||
* @param typeId 类型ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public Result<PageResult<PlaylistDTO>> getPlaylistList(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) Long typeId) {
|
||||
PageResult<PlaylistDTO> pageResult = playlistService.getPlaylistList(page, size, keyword, typeId);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新歌单列表
|
||||
*
|
||||
* @param limit 限制数量
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/latest")
|
||||
public Result<List<PlaylistDTO>> getLatestPlaylist(@RequestParam(defaultValue = "10") Integer limit) {
|
||||
List<PlaylistDTO> playlistList = playlistService.getLatestPlaylist(limit);
|
||||
return Result.success(playlistList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取热门歌单列表
|
||||
*
|
||||
* @param limit 限制数量
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/hot")
|
||||
public Result<List<PlaylistDTO>> getHotPlaylist(@RequestParam(defaultValue = "10") Integer limit) {
|
||||
List<PlaylistDTO> playlistList = playlistService.getHotPlaylist(limit);
|
||||
return Result.success(playlistList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索歌单
|
||||
*
|
||||
* @param keyword 关键字
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/search")
|
||||
public Result<PageResult<PlaylistDTO>> searchPlaylist(
|
||||
@RequestParam String keyword,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size) {
|
||||
PageResult<PlaylistDTO> pageResult = playlistService.getPlaylistList(page, size, keyword, null);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取官方歌单列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/official")
|
||||
public Result<PageResult<PlaylistDTO>> getOfficialPlaylist(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size) {
|
||||
PageResult<PlaylistDTO> pageResult = playlistService.getOfficialPlaylist(page, size);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加歌单
|
||||
*
|
||||
* @param playlistDTO 歌单DTO
|
||||
* @param cover 封面文件
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@PostMapping("/add")
|
||||
public Result<PlaylistDTO> addPlaylist(
|
||||
@RequestPart("playlist") PlaylistDTO playlistDTO,
|
||||
@RequestPart(value = "cover", required = false) MultipartFile cover) {
|
||||
PlaylistDTO result = playlistService.addPlaylist(playlistDTO, cover);
|
||||
return Result.success("添加成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新歌单
|
||||
*
|
||||
* @param id 歌单ID
|
||||
* @param playlistDTO 歌单DTO
|
||||
* @param cover 封面文件
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@PutMapping("/{id}")
|
||||
public Result<PlaylistDTO> updatePlaylist(
|
||||
@PathVariable Long id,
|
||||
@RequestPart("playlist") PlaylistDTO playlistDTO,
|
||||
@RequestPart(value = "cover", required = false) MultipartFile cover) {
|
||||
PlaylistDTO result = playlistService.updatePlaylist(id, playlistDTO, cover);
|
||||
return Result.success("更新成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除歌单
|
||||
*
|
||||
* @param id 歌单ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Boolean> deletePlaylist(@PathVariable Long id) {
|
||||
boolean result = playlistService.deletePlaylist(id);
|
||||
return Result.success("删除成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 收藏歌单
|
||||
*
|
||||
* @param id 歌单ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@PostMapping("/{id}/collect")
|
||||
public Result<Boolean> collectPlaylist(@PathVariable Long id) {
|
||||
boolean result = playlistService.collectPlaylist(id);
|
||||
return Result.success("收藏成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消收藏歌单
|
||||
*
|
||||
* @param id 歌单ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@DeleteMapping("/{id}/collect")
|
||||
public Result<Boolean> uncollectPlaylist(@PathVariable Long id) {
|
||||
boolean result = playlistService.uncollectPlaylist(id);
|
||||
return Result.success("取消收藏成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户收藏的歌单列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@GetMapping("/collect")
|
||||
public Result<PageResult<PlaylistDTO>> getCollectedPlaylist(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size) {
|
||||
PageResult<PlaylistDTO> pageResult = playlistService.getCollectedPlaylist(page, size);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户创建的歌单列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@GetMapping("/user")
|
||||
public Result<PageResult<PlaylistDTO>> getUserPlaylist(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size) {
|
||||
PageResult<PlaylistDTO> pageResult = playlistService.getUserPlaylist(page, size);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向歌单添加音乐
|
||||
*
|
||||
* @param id 歌单ID
|
||||
* @param musicId 音乐ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@PostMapping("/{id}/music/{musicId}")
|
||||
public Result<Boolean> addMusicToPlaylist(@PathVariable Long id, @PathVariable Long musicId) {
|
||||
boolean result = playlistService.addMusicToPlaylist(id, musicId);
|
||||
return Result.success("添加成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从歌单移除音乐
|
||||
*
|
||||
* @param id 歌单ID
|
||||
* @param musicId 音乐ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@DeleteMapping("/{id}/music/{musicId}")
|
||||
public Result<Boolean> removeMusicFromPlaylist(@PathVariable Long id, @PathVariable Long musicId) {
|
||||
boolean result = playlistService.removeMusicFromPlaylist(id, musicId);
|
||||
return Result.success("移除成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放歌单
|
||||
*
|
||||
* @param id 歌单ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@PostMapping("/{id}/play")
|
||||
public Result<PlaylistDTO> playPlaylist(@PathVariable Long id) {
|
||||
PlaylistDTO playlistDTO = playlistService.playPlaylist(id);
|
||||
return Result.success(playlistDTO);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
package com.test.musichouduan.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import com.test.musichouduan.dto.PageResult;
|
||||
import com.test.musichouduan.dto.Result;
|
||||
import com.test.musichouduan.dto.SingerDTO;
|
||||
import com.test.musichouduan.service.SingerService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 歌手控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/singer")
|
||||
public class SingerController {
|
||||
|
||||
@Autowired
|
||||
private SingerService singerService;
|
||||
|
||||
/**
|
||||
* 根据ID获取歌手
|
||||
*
|
||||
* @param id 歌手ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public Result<SingerDTO> getSingerById(@PathVariable Long id) {
|
||||
SingerDTO singerDTO = singerService.getSingerById(id);
|
||||
return Result.success(singerDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页获取歌手列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @param keyword 关键字
|
||||
* @param typeId 类型ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public Result<PageResult<SingerDTO>> getSingerList(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) Long typeId) {
|
||||
PageResult<SingerDTO> pageResult = singerService.getSingerList(page, size, keyword, typeId);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索歌手
|
||||
*
|
||||
* @param keyword 关键字
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/search")
|
||||
public Result<PageResult<SingerDTO>> searchSinger(
|
||||
@RequestParam String keyword,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size) {
|
||||
PageResult<SingerDTO> pageResult = singerService.getSingerList(page, size, keyword, null);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新歌手列表
|
||||
*
|
||||
* @param limit 限制数量
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/latest")
|
||||
public Result<List<SingerDTO>> getLatestSinger(@RequestParam(defaultValue = "10") Integer limit) {
|
||||
List<SingerDTO> singerList = singerService.getLatestSinger(limit);
|
||||
return Result.success(singerList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取热门歌手列表
|
||||
*
|
||||
* @param limit 限制数量
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/hot-list")
|
||||
public Result<List<SingerDTO>> getHotSinger(@RequestParam(defaultValue = "10") Integer limit) {
|
||||
List<SingerDTO> singerList = singerService.getHotSinger(limit);
|
||||
return Result.success(singerList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加歌手
|
||||
*
|
||||
* @param singerDTO 歌手DTO
|
||||
* @param avatar 头像文件
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@PostMapping("/add")
|
||||
public Result<SingerDTO> addSinger(
|
||||
@RequestPart("singer") SingerDTO singerDTO,
|
||||
@RequestPart(value = "avatar", required = false) MultipartFile avatar) {
|
||||
SingerDTO result = singerService.addSinger(singerDTO, avatar);
|
||||
return Result.success("添加成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新歌手
|
||||
*
|
||||
* @param id 歌手ID
|
||||
* @param singerDTO 歌手DTO
|
||||
* @param avatar 头像文件
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@PutMapping("/{id}")
|
||||
public Result<SingerDTO> updateSinger(
|
||||
@PathVariable Long id,
|
||||
@RequestPart("singer") SingerDTO singerDTO,
|
||||
@RequestPart(value = "avatar", required = false) MultipartFile avatar) {
|
||||
SingerDTO result = singerService.updateSinger(id, singerDTO, avatar);
|
||||
return Result.success("更新成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除歌手
|
||||
*
|
||||
* @param id 歌手ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Boolean> deleteSinger(@PathVariable Long id) {
|
||||
boolean result = singerService.deleteSinger(id);
|
||||
return Result.success("删除成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 收藏歌手
|
||||
*
|
||||
* @param id 歌手ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@PostMapping("/{id}/collect")
|
||||
public Result<Boolean> collectSinger(@PathVariable Long id) {
|
||||
boolean result = singerService.collectSinger(id);
|
||||
return Result.success("收藏成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消收藏歌手
|
||||
*
|
||||
* @param id 歌手ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@DeleteMapping("/{id}/collect")
|
||||
public Result<Boolean> uncollectSinger(@PathVariable Long id) {
|
||||
boolean result = singerService.uncollectSinger(id);
|
||||
return Result.success("取消收藏成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户收藏的歌手列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@GetMapping("/collect")
|
||||
public Result<PageResult<SingerDTO>> getCollectedSinger(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size) {
|
||||
PageResult<SingerDTO> pageResult = singerService.getCollectedSinger(page, size);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加音乐到歌手
|
||||
* @param singerId 歌手ID
|
||||
* @param musicId 音乐ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@PostMapping("/{singerId}/music/{musicId}")
|
||||
public Result<Boolean> addMusicToSinger(
|
||||
@PathVariable Long singerId,
|
||||
@PathVariable Long musicId) {
|
||||
boolean result = singerService.addMusicToSinger(singerId, musicId);
|
||||
return Result.success("添加成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从歌手移除音乐
|
||||
* @param singerId 歌手ID
|
||||
* @param musicId 音乐ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@DeleteMapping("/{singerId}/music/{musicId}")
|
||||
public Result<Boolean> removeMusicFromSinger(
|
||||
@PathVariable Long singerId,
|
||||
@PathVariable Long musicId) {
|
||||
boolean result = singerService.removeMusicFromSinger(singerId, musicId);
|
||||
return Result.success("移除成功", result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.test.musichouduan.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import com.test.musichouduan.dto.Result;
|
||||
import com.test.musichouduan.service.StatService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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 java.util.Map;
|
||||
|
||||
/**
|
||||
* 统计控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/stat")
|
||||
@SaCheckRole("admin")
|
||||
public class StatController {
|
||||
|
||||
@Autowired
|
||||
private StatService statService;
|
||||
|
||||
/**
|
||||
* 获取总体统计数据
|
||||
*
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/total")
|
||||
public Result<Map<String, Object>> getTotalStats() {
|
||||
Map<String, Object> stats = statService.getTotalStats();
|
||||
return Result.success(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户增长趋势
|
||||
*
|
||||
* @param days 天数
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/user/growth")
|
||||
public Result<Map<String, Object>> getUserGrowthTrend(@RequestParam(defaultValue = "7") Integer days) {
|
||||
Map<String, Object> stats = statService.getUserGrowthTrend(days);
|
||||
return Result.success(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取音乐类型分布
|
||||
*
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/music/type")
|
||||
public Result<Map<String, Object>> getMusicTypeDistribution() {
|
||||
Map<String, Object> stats = statService.getMusicTypeDistribution();
|
||||
return Result.success(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取播放量统计
|
||||
*
|
||||
* @param days 天数
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/play/count")
|
||||
public Result<Map<String, Object>> getPlayCountStats(@RequestParam(defaultValue = "7") Integer days) {
|
||||
Map<String, Object> stats = statService.getPlayCountStats(days);
|
||||
return Result.success(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新用户列表
|
||||
*
|
||||
* @param limit 限制数量
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/user/latest")
|
||||
public Result<Map<String, Object>> getLatestUsers(@RequestParam(defaultValue = "5") Integer limit) {
|
||||
Map<String, Object> stats = statService.getLatestUsers(limit);
|
||||
return Result.success(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新音乐列表
|
||||
*
|
||||
* @param limit 限制数量
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/music/latest")
|
||||
public Result<Map<String, Object>> getLatestMusics(@RequestParam(defaultValue = "5") Integer limit) {
|
||||
Map<String, Object> stats = statService.getLatestMusics(limit);
|
||||
return Result.success(stats);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
package com.test.musichouduan.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import com.test.musichouduan.dto.*;
|
||||
import com.test.musichouduan.service.TypeService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 类型控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/type")
|
||||
public class TypeController {
|
||||
|
||||
@Autowired
|
||||
private TypeService typeService;
|
||||
|
||||
/**
|
||||
* 获取音乐类型列表
|
||||
*
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/music")
|
||||
public Result<List<MusicTypeDTO>> getMusicTypeList() {
|
||||
List<MusicTypeDTO> typeList = typeService.getMusicTypeList();
|
||||
return Result.success(typeList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页获取音乐类型列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @param keyword 关键字
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@GetMapping("/music/page")
|
||||
public Result<PageResult<MusicTypeDTO>> getMusicTypePage(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size,
|
||||
@RequestParam(required = false) String keyword) {
|
||||
PageResult<MusicTypeDTO> pageResult = typeService.getMusicTypePage(page, size, keyword);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加音乐类型
|
||||
*
|
||||
* @param musicTypeDTO 音乐类型DTO
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@PostMapping("/music/add")
|
||||
public Result<MusicTypeDTO> addMusicType(@RequestBody MusicTypeDTO musicTypeDTO) {
|
||||
MusicTypeDTO result = typeService.addMusicType(musicTypeDTO);
|
||||
return Result.success("添加成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新音乐类型
|
||||
*
|
||||
* @param id 音乐类型ID
|
||||
* @param musicTypeDTO 音乐类型DTO
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@PutMapping("/music/{id}")
|
||||
public Result<MusicTypeDTO> updateMusicType(@PathVariable Long id, @RequestBody MusicTypeDTO musicTypeDTO) {
|
||||
MusicTypeDTO result = typeService.updateMusicType(id, musicTypeDTO);
|
||||
return Result.success("更新成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除音乐类型
|
||||
*
|
||||
* @param id 音乐类型ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@DeleteMapping("/music/{id}")
|
||||
public Result<Boolean> deleteMusicType(@PathVariable Long id) {
|
||||
boolean result = typeService.deleteMusicType(id);
|
||||
return Result.success("删除成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取歌手类型列表
|
||||
*
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/singer")
|
||||
public Result<List<SingerTypeDTO>> getSingerTypeList() {
|
||||
List<SingerTypeDTO> typeList = typeService.getSingerTypeList();
|
||||
return Result.success(typeList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页获取歌手类型列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @param keyword 关键字
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@GetMapping("/singer/page")
|
||||
public Result<PageResult<SingerTypeDTO>> getSingerTypePage(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size,
|
||||
@RequestParam(required = false) String keyword) {
|
||||
PageResult<SingerTypeDTO> pageResult = typeService.getSingerTypePage(page, size, keyword);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加歌手类型
|
||||
*
|
||||
* @param singerTypeDTO 歌手类型DTO
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@PostMapping("/singer/add")
|
||||
public Result<SingerTypeDTO> addSingerType(@RequestBody SingerTypeDTO singerTypeDTO) {
|
||||
SingerTypeDTO result = typeService.addSingerType(singerTypeDTO);
|
||||
return Result.success("添加成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新歌手类型
|
||||
*
|
||||
* @param id 歌手类型ID
|
||||
* @param singerTypeDTO 歌手类型DTO
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@PutMapping("/singer/{id}")
|
||||
public Result<SingerTypeDTO> updateSingerType(@PathVariable Long id, @RequestBody SingerTypeDTO singerTypeDTO) {
|
||||
SingerTypeDTO result = typeService.updateSingerType(id, singerTypeDTO);
|
||||
return Result.success("更新成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除歌手类型
|
||||
*
|
||||
* @param id 歌手类型ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@DeleteMapping("/singer/{id}")
|
||||
public Result<Boolean> deleteSingerType(@PathVariable Long id) {
|
||||
boolean result = typeService.deleteSingerType(id);
|
||||
return Result.success("删除成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取歌单类型列表
|
||||
*
|
||||
* @return 响应结果
|
||||
*/
|
||||
@GetMapping("/playlist")
|
||||
public Result<List<PlaylistTypeDTO>> getPlaylistTypeList() {
|
||||
List<PlaylistTypeDTO> typeList = typeService.getPlaylistTypeList();
|
||||
return Result.success(typeList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页获取歌单类型列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @param keyword 关键字
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@GetMapping("/playlist/page")
|
||||
public Result<PageResult<PlaylistTypeDTO>> getPlaylistTypePage(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer size,
|
||||
@RequestParam(required = false) String keyword) {
|
||||
PageResult<PlaylistTypeDTO> pageResult = typeService.getPlaylistTypePage(page, size, keyword);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加歌单类型
|
||||
*
|
||||
* @param playlistTypeDTO 歌单类型DTO
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@PostMapping("/playlist/add")
|
||||
public Result<PlaylistTypeDTO> addPlaylistType(@RequestBody PlaylistTypeDTO playlistTypeDTO) {
|
||||
PlaylistTypeDTO result = typeService.addPlaylistType(playlistTypeDTO);
|
||||
return Result.success("添加成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新歌单类型
|
||||
*
|
||||
* @param id 歌单类型ID
|
||||
* @param playlistTypeDTO 歌单类型DTO
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@PutMapping("/playlist/{id}")
|
||||
public Result<PlaylistTypeDTO> updatePlaylistType(@PathVariable Long id, @RequestBody PlaylistTypeDTO playlistTypeDTO) {
|
||||
PlaylistTypeDTO result = typeService.updatePlaylistType(id, playlistTypeDTO);
|
||||
return Result.success("更新成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除歌单类型
|
||||
*
|
||||
* @param id 歌单类型ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@SaCheckRole("admin")
|
||||
@DeleteMapping("/playlist/{id}")
|
||||
public Result<Boolean> deletePlaylistType(@PathVariable Long id) {
|
||||
boolean result = typeService.deletePlaylistType(id);
|
||||
return Result.success("删除成功", result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
package com.test.musichouduan.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.test.musichouduan.dto.*;
|
||||
import com.test.musichouduan.service.FileService;
|
||||
import com.test.musichouduan.service.UserService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 用户控制器
|
||||
*/
|
||||
@Tag(name = "用户接口", description = "用户的注册、登录、信息管理等操作")
|
||||
@RestController
|
||||
@RequestMapping("/api/user")
|
||||
public class UserController {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private FileService fileService;
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
*
|
||||
* @param request 注册请求
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "用户注册", description = "新用户注册接口")
|
||||
@PostMapping("/register")
|
||||
public Result<UserDTO> register(@Parameter(description = "注册信息") @Validated @RequestBody RegisterRequest request) {
|
||||
UserDTO userDTO = userService.register(request);
|
||||
return Result.success("注册成功", userDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
*
|
||||
* @param request 登录请求
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "用户登录", description = "用户登录接口,登录成功后返回用户信息和令牌")
|
||||
@PostMapping("/login")
|
||||
public Result<LoginResultDTO> login(@Parameter(description = "登录信息") @Validated @RequestBody LoginRequest request) {
|
||||
LoginResultDTO resultDTO = userService.login(request);
|
||||
return Result.success("登录成功", resultDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登出
|
||||
*
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "用户登出", description = "用户登出接口,需要登录状态")
|
||||
@SaCheckLogin
|
||||
@PostMapping("/logout")
|
||||
public Result<String> logout() {
|
||||
StpUtil.logout();
|
||||
return Result.success("登出成功", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户
|
||||
*
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "获取当前登录用户", description = "获取当前登录用户的信息,需要登录状态")
|
||||
@SaCheckLogin
|
||||
@GetMapping("/current")
|
||||
public Result<UserDTO> getCurrentUser() {
|
||||
UserDTO userDTO = userService.getCurrentUser();
|
||||
return Result.success(userDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
*
|
||||
* @param request 用户信息更新请求
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "更新用户信息", description = "更新当前登录用户的信息,需要登录状态")
|
||||
@SaCheckLogin
|
||||
@PutMapping("/update")
|
||||
public Result<UserDTO> updateUser(@Parameter(description = "用户信息更新请求") @Validated @RequestBody UserUpdateRequest request) {
|
||||
UserDTO userDTO = userService.updateUser(request);
|
||||
return Result.success("更新成功", userDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*
|
||||
* @param request 修改密码请求
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "修改密码", description = "修改当前登录用户的密码,需要登录状态")
|
||||
@SaCheckLogin
|
||||
@PutMapping("/password")
|
||||
public Result<Boolean> updatePassword(@Parameter(description = "密码修改请求") @Validated @RequestBody PasswordUpdateRequest request) {
|
||||
boolean result = userService.updatePassword(request);
|
||||
return Result.success("修改成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户统计数据
|
||||
*
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "获取用户统计数据", description = "获取当前登录用户的统计数据,包括收藏数量等,需要登录状态")
|
||||
@SaCheckLogin
|
||||
@GetMapping("/stats")
|
||||
public Result<UserDTO> getUserStats() {
|
||||
UserDTO userDTO = userService.getUserStats(StpUtil.getLoginIdAsLong());
|
||||
return Result.success(userDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页获取用户列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @param keyword 关键字
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "分页获取用户列表", description = "分页获取用户列表,支持关键字搜索,需要管理员权限")
|
||||
@SaCheckRole("admin")
|
||||
@GetMapping("/list")
|
||||
public Result<PageResult<UserDTO>> getUserList(
|
||||
@Parameter(description = "页码", example = "1") @RequestParam(defaultValue = "1") Integer page,
|
||||
@Parameter(description = "每页大小", example = "10") @RequestParam(defaultValue = "10") Integer size,
|
||||
@Parameter(description = "搜索关键字", example = "用户名") @RequestParam(required = false) String keyword) {
|
||||
PageResult<UserDTO> pageResult = userService.getUserList(page, size, keyword);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取用户
|
||||
*
|
||||
* @param id 用户ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "根据ID获取用户", description = "根据用户ID获取用户信息,需要管理员权限")
|
||||
@SaCheckRole("admin")
|
||||
@GetMapping("/{id}")
|
||||
public Result<UserDTO> getUserById(@Parameter(description = "用户ID", example = "1") @PathVariable Long id) {
|
||||
UserDTO userDTO = userService.getUserById(id);
|
||||
return Result.success(userDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
*
|
||||
* @param id 用户ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "删除用户", description = "根据用户ID删除用户,需要管理员权限")
|
||||
@SaCheckRole("admin")
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Boolean> deleteUser(@Parameter(description = "用户ID", example = "1") @PathVariable Long id) {
|
||||
boolean result = userService.deleteUser(id);
|
||||
return Result.success("删除成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户状态
|
||||
*
|
||||
* @param id 用户ID
|
||||
* @param status 状态
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "更新用户状态", description = "更新用户状态,0-禁用,1-启用,需要管理员权限")
|
||||
@SaCheckRole("admin")
|
||||
@PutMapping("/{id}/status")
|
||||
public Result<Boolean> updateUserStatus(
|
||||
@Parameter(description = "用户ID", example = "1") @PathVariable Long id,
|
||||
@Parameter(description = "状态,0-禁用,1-启用", example = "1") @RequestParam Integer status) {
|
||||
boolean result = userService.updateUserStatus(id, status);
|
||||
return Result.success("更新成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员更新用户信息
|
||||
*
|
||||
* @param id 用户ID
|
||||
* @param request 用户信息更新请求
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "管理员更新用户信息", description = "管理员更新用户信息,需要管理员权限")
|
||||
@SaCheckRole("admin")
|
||||
@PutMapping("/{id}")
|
||||
public Result<UserDTO> adminUpdateUser(
|
||||
@Parameter(description = "用户ID", example = "1") @PathVariable Long id,
|
||||
@Parameter(description = "用户信息更新请求") @Validated @RequestBody UserUpdateRequest request) {
|
||||
UserDTO userDTO = userService.adminUpdateUser(id, request);
|
||||
return Result.success("更新成功", userDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传用户头像
|
||||
*
|
||||
* @param file 头像文件
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "上传用户头像", description = "上传当前登录用户的头像,需要登录状态")
|
||||
@SaCheckLogin
|
||||
@PostMapping("/upload/avatar")
|
||||
public Result<String> uploadAvatar(@Parameter(description = "头像文件") @RequestParam("file") MultipartFile file) {
|
||||
// 上传头像
|
||||
String avatarUrl = fileService.uploadImage(file);
|
||||
|
||||
// 用户ID已经通过 SaCheckLogin 注解验证
|
||||
|
||||
// 更新用户头像
|
||||
UserUpdateRequest request = new UserUpdateRequest();
|
||||
request.setAvatar(avatarUrl);
|
||||
userService.updateUser(request);
|
||||
|
||||
return Result.success("上传成功", avatarUrl);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.test.musichouduan.controller.admin;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import com.test.musichouduan.dto.CommentDTO;
|
||||
import com.test.musichouduan.dto.PageResult;
|
||||
import com.test.musichouduan.dto.Result;
|
||||
import com.test.musichouduan.service.CommentService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 管理员评论控制器
|
||||
*/
|
||||
@Tag(name = "管理员评论接口", description = "提供管理员评论管理功能")
|
||||
@RestController
|
||||
@RequestMapping("/api/admin/comment")
|
||||
@SaCheckRole("admin")
|
||||
public class AdminCommentController {
|
||||
|
||||
@Autowired
|
||||
private CommentService commentService;
|
||||
|
||||
/**
|
||||
* 分页获取所有评论
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @param keyword 关键字
|
||||
* @param targetType 目标类型
|
||||
* @param status 状态
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "分页获取所有评论", description = "管理员分页获取所有评论")
|
||||
@GetMapping("/list")
|
||||
public Result<PageResult<CommentDTO>> getCommentList(
|
||||
@Parameter(description = "页码", example = "1") @RequestParam(defaultValue = "1") Integer page,
|
||||
@Parameter(description = "每页大小", example = "10") @RequestParam(defaultValue = "10") Integer size,
|
||||
@Parameter(description = "关键字") @RequestParam(required = false) String keyword,
|
||||
@Parameter(description = "目标类型:1-音乐,2-歌单,3-歌手") @RequestParam(required = false) Integer targetType,
|
||||
@Parameter(description = "状态:0-禁用,1-正常") @RequestParam(required = false) Integer status) {
|
||||
PageResult<CommentDTO> pageResult = commentService.getAdminCommentList(page, size, keyword, targetType, status);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新评论状态
|
||||
*
|
||||
* @param id 评论ID
|
||||
* @param status 状态:0-禁用,1-正常
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "更新评论状态", description = "管理员更新评论状态")
|
||||
@PutMapping("/{id}/status")
|
||||
public Result<Boolean> updateCommentStatus(
|
||||
@Parameter(description = "评论ID") @PathVariable Long id,
|
||||
@Parameter(description = "状态:0-禁用,1-正常", example = "1") @RequestParam Integer status) {
|
||||
boolean result = commentService.updateCommentStatus(id, status);
|
||||
return Result.success("更新成功", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除评论
|
||||
*
|
||||
* @param id 评论ID
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Operation(summary = "删除评论", description = "管理员删除评论")
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Boolean> deleteComment(@Parameter(description = "评论ID") @PathVariable Long id) {
|
||||
boolean result = commentService.adminDeleteComment(id);
|
||||
return Result.success("删除成功", result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
package com.test.musichouduan.controller.admin;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import com.test.musichouduan.dto.Result;
|
||||
import com.test.musichouduan.dto.admin.DashboardDataDTO;
|
||||
import com.test.musichouduan.dto.admin.MusicAdminDTO;
|
||||
import com.test.musichouduan.dto.admin.StatItemDTO;
|
||||
import com.test.musichouduan.dto.admin.UserAdminDTO;
|
||||
import com.test.musichouduan.entity.Music;
|
||||
import com.test.musichouduan.entity.PlaylistType;
|
||||
import com.test.musichouduan.entity.User;
|
||||
import com.test.musichouduan.repository.MusicRepository;
|
||||
import com.test.musichouduan.repository.PlayHistoryRepository;
|
||||
import com.test.musichouduan.repository.PlaylistRepository;
|
||||
import com.test.musichouduan.repository.SingerRepository;
|
||||
import com.test.musichouduan.repository.UserRepository;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 后台管理控制台控制器
|
||||
*/
|
||||
@Tag(name = "后台管理控制台接口", description = "提供后台管理控制台所需的统计数据")
|
||||
@RestController
|
||||
@RequestMapping("/api/admin/dashboard")
|
||||
@SaCheckRole("admin")
|
||||
public class AdminDashboardController {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
private MusicRepository musicRepository;
|
||||
|
||||
@Autowired
|
||||
private PlaylistRepository playlistRepository;
|
||||
|
||||
@Autowired
|
||||
private SingerRepository singerRepository;
|
||||
|
||||
@Autowired
|
||||
private PlayHistoryRepository playHistoryRepository;
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager entityManager;
|
||||
|
||||
/**
|
||||
* 获取控制台统计数据
|
||||
*
|
||||
* @return 统计数据
|
||||
*/
|
||||
@Operation(summary = "获取控制台统计数据", description = "获取用户数量、音乐数量、歌单数量、歌手数量等统计数据")
|
||||
@GetMapping("/stats")
|
||||
public Result<DashboardDataDTO> getStats() {
|
||||
try {
|
||||
DashboardDataDTO data = new DashboardDataDTO();
|
||||
|
||||
// 统计基础数据
|
||||
long userCount = userRepository.count();
|
||||
long musicCount = musicRepository.count();
|
||||
long playlistCount = playlistRepository.count();
|
||||
long singerCount = singerRepository.count();
|
||||
long playCount = musicRepository.sumPlayCount();
|
||||
|
||||
data.setUserCount(userCount);
|
||||
data.setMusicCount(musicCount);
|
||||
data.setPlaylistCount(playlistCount);
|
||||
data.setSingerCount(singerCount);
|
||||
data.setPlayCount(playCount);
|
||||
|
||||
// 获取最近7天的用户增长数据
|
||||
data.setUserGrowth(getUserGrowthData());
|
||||
|
||||
// 获取音乐类型分布数据
|
||||
data.setMusicTypeDistribution(getTypeDistribution());
|
||||
|
||||
// 获取最近7天的播放量数据
|
||||
data.setPlayCountData(getPlayCountData());
|
||||
|
||||
// 获取最新用户
|
||||
List<User> users = userRepository.findLatestUsers(PageRequest.of(0, 5));
|
||||
List<UserAdminDTO> latestUsers = users.stream()
|
||||
.map(UserAdminDTO::fromEntity)
|
||||
.collect(Collectors.toList());
|
||||
data.setLatestUsers(latestUsers);
|
||||
|
||||
// 获取最新音乐
|
||||
List<Music> musics = musicRepository.findLatest(PageRequest.of(0, 5));
|
||||
List<MusicAdminDTO> latestMusics = musics.stream()
|
||||
.map(MusicAdminDTO::fromEntity)
|
||||
.collect(Collectors.toList());
|
||||
data.setLatestMusics(latestMusics);
|
||||
|
||||
return Result.success(data);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return Result.error("获取控制台统计数据失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近7天的用户增长数据
|
||||
*
|
||||
* @return 用户增长数据
|
||||
*/
|
||||
private List<StatItemDTO> getUserGrowthData() {
|
||||
List<StatItemDTO> result = new ArrayList<>();
|
||||
LocalDate today = LocalDate.now();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd");
|
||||
|
||||
// 获取最近7天的日期
|
||||
for (int i = 6; i >= 0; i--) {
|
||||
LocalDate date = today.minusDays(i);
|
||||
LocalDateTime startOfDay = date.atStartOfDay();
|
||||
LocalDateTime endOfDay = date.plusDays(1).atStartOfDay().minusNanos(1);
|
||||
|
||||
// 统计当天注册的用户数量
|
||||
long count = userRepository.countByCreateTimeBetween(startOfDay, endOfDay);
|
||||
|
||||
StatItemDTO item = new StatItemDTO();
|
||||
item.setName(date.format(formatter));
|
||||
item.setValue(count);
|
||||
result.add(item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取音乐、歌单、歌手类型分布数据
|
||||
*
|
||||
* @return 类型分布数据
|
||||
*/
|
||||
private List<StatItemDTO> getTypeDistribution() {
|
||||
List<StatItemDTO> result = new ArrayList<>();
|
||||
|
||||
// 音乐类型分布
|
||||
StatItemDTO musicTypeStat = new StatItemDTO();
|
||||
musicTypeStat.setName("音乐类型");
|
||||
|
||||
try {
|
||||
List<Object[]> musicTypeData = musicRepository.countByMusicType();
|
||||
List<StatItemDTO> musicTypeChildren = musicTypeData.stream().map(item -> {
|
||||
StatItemDTO child = new StatItemDTO();
|
||||
child.setName((String) item[0]);
|
||||
child.setValue(((Number) item[1]).longValue());
|
||||
return child;
|
||||
}).collect(Collectors.toList());
|
||||
musicTypeStat.setChildren(musicTypeChildren);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
// 如果查询失败,返回空列表
|
||||
musicTypeStat.setChildren(new ArrayList<>());
|
||||
}
|
||||
|
||||
result.add(musicTypeStat);
|
||||
|
||||
// 歌单类型分布
|
||||
StatItemDTO playlistTypeStat = new StatItemDTO();
|
||||
playlistTypeStat.setName("歌单类型");
|
||||
|
||||
// 使用原生 SQL 查询或者手动聚合数据
|
||||
List<PlaylistType> playlistTypes = entityManager.createQuery("SELECT DISTINCT pt FROM PlaylistType pt", PlaylistType.class).getResultList();
|
||||
List<StatItemDTO> playlistTypeChildren = new ArrayList<>();
|
||||
|
||||
for (PlaylistType type : playlistTypes) {
|
||||
Long count = playlistRepository.countByType(type);
|
||||
StatItemDTO child = new StatItemDTO();
|
||||
child.setName(type.getName());
|
||||
child.setValue(count);
|
||||
playlistTypeChildren.add(child);
|
||||
}
|
||||
|
||||
playlistTypeStat.setChildren(playlistTypeChildren);
|
||||
result.add(playlistTypeStat);
|
||||
|
||||
// 歌手类型分布
|
||||
StatItemDTO singerTypeStat = new StatItemDTO();
|
||||
singerTypeStat.setName("歌手类型");
|
||||
|
||||
// 使用原生 SQL 查询或者手动聚合数据
|
||||
List<com.test.musichouduan.entity.SingerType> singerTypes = entityManager.createQuery("SELECT DISTINCT st FROM SingerType st", com.test.musichouduan.entity.SingerType.class).getResultList();
|
||||
List<StatItemDTO> singerTypeChildren = new ArrayList<>();
|
||||
|
||||
for (com.test.musichouduan.entity.SingerType type : singerTypes) {
|
||||
Long count = singerRepository.countByType(type);
|
||||
StatItemDTO child = new StatItemDTO();
|
||||
child.setName(type.getName());
|
||||
child.setValue(count);
|
||||
singerTypeChildren.add(child);
|
||||
}
|
||||
|
||||
singerTypeStat.setChildren(singerTypeChildren);
|
||||
result.add(singerTypeStat);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近7天的播放量数据
|
||||
*
|
||||
* @return 播放量数据
|
||||
*/
|
||||
private List<StatItemDTO> getPlayCountData() {
|
||||
List<StatItemDTO> result = new ArrayList<>();
|
||||
LocalDate today = LocalDate.now();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd");
|
||||
|
||||
// 获取最近7天的日期
|
||||
for (int i = 6; i >= 0; i--) {
|
||||
LocalDate date = today.minusDays(i);
|
||||
LocalDateTime startOfDay = date.atStartOfDay();
|
||||
LocalDateTime endOfDay = date.plusDays(1).atStartOfDay().minusNanos(1);
|
||||
|
||||
// 统计当天的播放量
|
||||
long count = playHistoryRepository.countByPlayTimeBetween(startOfDay, endOfDay);
|
||||
|
||||
StatItemDTO item = new StatItemDTO();
|
||||
item.setName(date.format(formatter));
|
||||
item.setValue(count);
|
||||
result.add(item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.test.musichouduan.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 轮播图数据传输对象
|
||||
*/
|
||||
@Data
|
||||
public class BannerDTO {
|
||||
|
||||
/**
|
||||
* 轮播图ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 图片URL
|
||||
*/
|
||||
private String imageUrl;
|
||||
|
||||
/**
|
||||
* 链接URL
|
||||
*/
|
||||
private String linkUrl;
|
||||
|
||||
/**
|
||||
* 目标ID
|
||||
*/
|
||||
private Long targetId;
|
||||
|
||||
/**
|
||||
* 目标类型:1-音乐,2-歌单,3-歌手
|
||||
*/
|
||||
private Integer targetType;
|
||||
|
||||
/**
|
||||
* 排序号
|
||||
*/
|
||||
private Integer orderNum;
|
||||
|
||||
/**
|
||||
* 状态:0-禁用,1-正常
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.test.musichouduan.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 评论数据传输对象
|
||||
*/
|
||||
@Data
|
||||
public class CommentDTO {
|
||||
|
||||
/**
|
||||
* 评论ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 评论内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 评论用户
|
||||
*/
|
||||
private UserDTO user;
|
||||
|
||||
/**
|
||||
* 评论目标ID
|
||||
*/
|
||||
private Long targetId;
|
||||
|
||||
/**
|
||||
* 评论目标类型:1-音乐,2-歌单,3-歌手
|
||||
*/
|
||||
private Integer targetType;
|
||||
|
||||
/**
|
||||
* 父评论ID
|
||||
*/
|
||||
private Long parentId;
|
||||
|
||||
/**
|
||||
* 子评论列表
|
||||
*/
|
||||
private List<CommentDTO> children;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.test.musichouduan.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 登录请求
|
||||
*/
|
||||
@Data
|
||||
public class LoginRequest {
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.test.musichouduan.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 登录结果数据传输对象
|
||||
*/
|
||||
@Data
|
||||
public class LoginResultDTO {
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
private UserDTO user;
|
||||
|
||||
/**
|
||||
* 访问令牌
|
||||
*/
|
||||
private String token;
|
||||
|
||||
/**
|
||||
* 令牌名称(用于请求头)
|
||||
*/
|
||||
private String tokenName;
|
||||
|
||||
/**
|
||||
* 令牌头前缀
|
||||
*/
|
||||
private String tokenPrefix;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.test.musichouduan.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 音乐数据传输对象
|
||||
*/
|
||||
@Data
|
||||
public class MusicDTO {
|
||||
|
||||
/**
|
||||
* 音乐ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 音乐名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 封面URL
|
||||
*/
|
||||
private String cover;
|
||||
|
||||
/**
|
||||
* 音乐文件URL
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 歌词
|
||||
*/
|
||||
private String lyric;
|
||||
|
||||
/**
|
||||
* 时长(秒)
|
||||
*/
|
||||
private Integer duration;
|
||||
|
||||
/**
|
||||
* 专辑
|
||||
*/
|
||||
private String album;
|
||||
|
||||
/**
|
||||
* 播放次数
|
||||
*/
|
||||
private Long playCount;
|
||||
|
||||
/**
|
||||
* 歌手列表
|
||||
*/
|
||||
private List<SingerDTO> singers;
|
||||
|
||||
/**
|
||||
* 歌手ID列表
|
||||
*/
|
||||
private List<Long> singerIds;
|
||||
|
||||
/**
|
||||
* 歌手名称列表
|
||||
*/
|
||||
private List<String> singerNames;
|
||||
|
||||
/**
|
||||
* 歌手名称(字符串)
|
||||
*/
|
||||
private String singer;
|
||||
|
||||
/**
|
||||
* 音乐类型列表
|
||||
*/
|
||||
private List<MusicTypeDTO> types;
|
||||
|
||||
/**
|
||||
* 音乐类型ID列表
|
||||
*/
|
||||
private List<Long> typeIds;
|
||||
|
||||
/**
|
||||
* 音乐类型名称列表
|
||||
*/
|
||||
private List<String> typeNames;
|
||||
|
||||
/**
|
||||
* 是否已收藏
|
||||
*/
|
||||
private Boolean isCollected;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 收藏时间
|
||||
*/
|
||||
private LocalDateTime collectTime;
|
||||
|
||||
/**
|
||||
* 设置是否已收藏
|
||||
*/
|
||||
public void setCollected(boolean collected) {
|
||||
this.isCollected = collected;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.test.musichouduan.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 音乐类型数据传输对象
|
||||
*/
|
||||
@Data
|
||||
public class MusicTypeDTO {
|
||||
|
||||
/**
|
||||
* 类型ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 类型名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 类型描述
|
||||
*/
|
||||
private String description;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.test.musichouduan.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分页结果
|
||||
*
|
||||
* @param <T> 数据类型
|
||||
*/
|
||||
@Data
|
||||
public class PageResult<T> {
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
*/
|
||||
private List<T> list;
|
||||
|
||||
/**
|
||||
* 总数
|
||||
*/
|
||||
private Long total;
|
||||
|
||||
/**
|
||||
* 当前页
|
||||
*/
|
||||
private Integer page;
|
||||
|
||||
/**
|
||||
* 每页大小
|
||||
*/
|
||||
private Integer size;
|
||||
|
||||
/**
|
||||
* 总页数
|
||||
*/
|
||||
private Integer totalPages;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
*
|
||||
* @param list 数据列表
|
||||
* @param total 总数
|
||||
* @param page 当前页
|
||||
* @param size 每页大小
|
||||
*/
|
||||
public PageResult(List<T> list, Long total, Integer page, Integer size) {
|
||||
this.list = list;
|
||||
this.total = total;
|
||||
this.page = page;
|
||||
this.size = size;
|
||||
this.totalPages = (int) Math.ceil((double) total / size);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.test.musichouduan.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 修改密码请求
|
||||
*/
|
||||
@Data
|
||||
public class PasswordUpdateRequest {
|
||||
|
||||
/**
|
||||
* 旧密码
|
||||
*/
|
||||
@NotBlank(message = "旧密码不能为空")
|
||||
private String oldPassword;
|
||||
|
||||
/**
|
||||
* 新密码
|
||||
*/
|
||||
@NotBlank(message = "新密码不能为空")
|
||||
@Size(min = 6, max = 20, message = "密码长度应为6-20个字符")
|
||||
private String newPassword;
|
||||
|
||||
/**
|
||||
* 确认新密码
|
||||
*/
|
||||
@NotBlank(message = "确认新密码不能为空")
|
||||
private String confirmPassword;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.test.musichouduan.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 歌单数据传输对象
|
||||
*/
|
||||
@Data
|
||||
public class PlaylistDTO {
|
||||
|
||||
/**
|
||||
* 歌单ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 歌单名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 封面URL
|
||||
*/
|
||||
private String cover;
|
||||
|
||||
/**
|
||||
* 歌单描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
private UserDTO creator;
|
||||
|
||||
/**
|
||||
* 歌单类型
|
||||
*/
|
||||
private PlaylistTypeDTO type;
|
||||
|
||||
/**
|
||||
* 是否官方歌单:0-否,1-是
|
||||
*/
|
||||
private Integer isOfficial;
|
||||
|
||||
/**
|
||||
* 播放次数
|
||||
*/
|
||||
private Long playCount;
|
||||
|
||||
/**
|
||||
* 歌单中的音乐
|
||||
*/
|
||||
private List<MusicDTO> musics;
|
||||
|
||||
/**
|
||||
* 音乐数量
|
||||
*/
|
||||
private Integer musicCount;
|
||||
|
||||
/**
|
||||
* 是否已收藏
|
||||
*/
|
||||
private Boolean isCollected;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.test.musichouduan.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 歌单类型数据传输对象
|
||||
*/
|
||||
@Data
|
||||
public class PlaylistTypeDTO {
|
||||
|
||||
/**
|
||||
* 类型ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 类型名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 类型描述
|
||||
*/
|
||||
private String description;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.test.musichouduan.dto;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 注册请求
|
||||
*/
|
||||
@Data
|
||||
public class RegisterRequest {
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
@Size(min = 3, max = 20, message = "用户名长度应为3-20个字符")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Size(min = 6, max = 20, message = "密码长度应为6-20个字符")
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 确认密码
|
||||
*/
|
||||
@NotBlank(message = "确认密码不能为空")
|
||||
private String confirmPassword;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
@Email(message = "邮箱格式不正确")
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
private String nickname;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.test.musichouduan.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 通用响应结果
|
||||
*
|
||||
* @param <T> 数据类型
|
||||
*/
|
||||
@Data
|
||||
public class Result<T> {
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
private Integer code;
|
||||
|
||||
/**
|
||||
* 消息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 数据
|
||||
*/
|
||||
private T data;
|
||||
|
||||
/**
|
||||
* 成功
|
||||
*
|
||||
* @param data 数据
|
||||
* @param <T> 数据类型
|
||||
* @return 响应结果
|
||||
*/
|
||||
public static <T> Result<T> success(T data) {
|
||||
Result<T> result = new Result<>();
|
||||
result.setCode(200);
|
||||
result.setMessage("success");
|
||||
result.setData(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功
|
||||
*
|
||||
* @param message 消息
|
||||
* @param data 数据
|
||||
* @param <T> 数据类型
|
||||
* @return 响应结果
|
||||
*/
|
||||
public static <T> Result<T> success(String message, T data) {
|
||||
Result<T> result = new Result<>();
|
||||
result.setCode(200);
|
||||
result.setMessage(message);
|
||||
result.setData(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*
|
||||
* @param code 状态码
|
||||
* @param message 消息
|
||||
* @param <T> 数据类型
|
||||
* @return 响应结果
|
||||
*/
|
||||
public static <T> Result<T> error(Integer code, String message) {
|
||||
Result<T> result = new Result<>();
|
||||
result.setCode(code);
|
||||
result.setMessage(message);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*
|
||||
* @param message 消息
|
||||
* @param <T> 数据类型
|
||||
* @return 响应结果
|
||||
*/
|
||||
public static <T> Result<T> error(String message) {
|
||||
return error(500, message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.test.musichouduan.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 歌手数据传输对象
|
||||
*/
|
||||
@Data
|
||||
public class SingerDTO {
|
||||
|
||||
/**
|
||||
* 歌手ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 歌手名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 歌手头像URL
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 歌手简介
|
||||
*/
|
||||
private String introduction;
|
||||
|
||||
/**
|
||||
* 歌手类型
|
||||
*/
|
||||
private SingerTypeDTO type;
|
||||
|
||||
/**
|
||||
* 音乐数量
|
||||
*/
|
||||
private Long musicCount;
|
||||
|
||||
/**
|
||||
* 粉丝数量
|
||||
*/
|
||||
private Long fansCount;
|
||||
|
||||
/**
|
||||
* 是否已收藏
|
||||
*/
|
||||
private Boolean isCollected;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 关联音乐列表
|
||||
*/
|
||||
private List<MusicDTO> musics;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.test.musichouduan.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 歌手类型数据传输对象
|
||||
*/
|
||||
@Data
|
||||
public class SingerTypeDTO {
|
||||
|
||||
/**
|
||||
* 类型ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 类型名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 类型描述
|
||||
*/
|
||||
private String description;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.test.musichouduan.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户数据传输对象
|
||||
*/
|
||||
@Data
|
||||
public class UserDTO {
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 头像URL
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 性别:0-未知,1-男,2-女
|
||||
*/
|
||||
private Integer gender;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 个人简介
|
||||
*/
|
||||
private String introduction;
|
||||
|
||||
/**
|
||||
* 角色:0-普通用户,1-管理员
|
||||
*/
|
||||
private Integer role;
|
||||
|
||||
/**
|
||||
* 状态:0-禁用,1-正常
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 收藏音乐数量
|
||||
*/
|
||||
private Long collectMusicCount;
|
||||
|
||||
/**
|
||||
* 收藏歌单数量
|
||||
*/
|
||||
private Long collectPlaylistCount;
|
||||
|
||||
/**
|
||||
* 收藏歌手数量
|
||||
*/
|
||||
private Long collectSingerCount;
|
||||
|
||||
/**
|
||||
* 创建歌单数量
|
||||
*/
|
||||
private Long createPlaylistCount;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.test.musichouduan.dto;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 用户信息更新请求
|
||||
*/
|
||||
@Data
|
||||
public class UserUpdateRequest {
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
@Size(max = 50, message = "昵称长度不能超过50个字符")
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 头像URL
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 性别:0-未知,1-男,2-女
|
||||
*/
|
||||
private Integer gender;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
@Email(message = "邮箱格式不正确")
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
@Size(max = 20, message = "手机号长度不能超过20个字符")
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 个人简介
|
||||
*/
|
||||
@Size(max = 500, message = "个人简介长度不能超过500个字符")
|
||||
private String introduction;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.test.musichouduan.dto.admin;
|
||||
|
||||
// 不需要导入同包下的类
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 控制台数据传输对象
|
||||
*/
|
||||
@Data
|
||||
public class DashboardDataDTO {
|
||||
|
||||
/**
|
||||
* 用户数量
|
||||
*/
|
||||
private long userCount;
|
||||
|
||||
/**
|
||||
* 音乐数量
|
||||
*/
|
||||
private long musicCount;
|
||||
|
||||
/**
|
||||
* 歌单数量
|
||||
*/
|
||||
private long playlistCount;
|
||||
|
||||
/**
|
||||
* 歌手数量
|
||||
*/
|
||||
private long singerCount;
|
||||
|
||||
/**
|
||||
* 总播放量
|
||||
*/
|
||||
private long playCount;
|
||||
|
||||
/**
|
||||
* 用户增长数据
|
||||
*/
|
||||
private List<StatItemDTO> userGrowth;
|
||||
|
||||
/**
|
||||
* 音乐类型分布数据
|
||||
*/
|
||||
private List<StatItemDTO> musicTypeDistribution;
|
||||
|
||||
/**
|
||||
* 播放量数据
|
||||
*/
|
||||
private List<StatItemDTO> playCountData;
|
||||
|
||||
/**
|
||||
* 最新用户
|
||||
*/
|
||||
private List<UserAdminDTO> latestUsers;
|
||||
|
||||
/**
|
||||
* 最新音乐
|
||||
*/
|
||||
private List<MusicAdminDTO> latestMusics;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.test.musichouduan.dto.admin;
|
||||
|
||||
import com.test.musichouduan.entity.Music;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 音乐管理数据传输对象
|
||||
*/
|
||||
@Data
|
||||
public class MusicAdminDTO {
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 音乐名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 歌手名称
|
||||
*/
|
||||
private String singerName;
|
||||
|
||||
/**
|
||||
* 封面URL
|
||||
*/
|
||||
private String coverUrl;
|
||||
|
||||
/**
|
||||
* 播放量
|
||||
*/
|
||||
private Long playCount;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 将实体转换为DTO
|
||||
*
|
||||
* @param music 音乐实体
|
||||
* @return 音乐DTO
|
||||
*/
|
||||
public static MusicAdminDTO fromEntity(Music music) {
|
||||
if (music == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MusicAdminDTO dto = new MusicAdminDTO();
|
||||
dto.setId(music.getId());
|
||||
dto.setName(music.getName());
|
||||
// 获取歌手名称
|
||||
String singerName = null;
|
||||
if (music.getSingers() != null && !music.getSingers().isEmpty()) {
|
||||
singerName = music.getSingers().iterator().next().getName();
|
||||
}
|
||||
dto.setSingerName(singerName);
|
||||
dto.setCoverUrl(music.getCover());
|
||||
dto.setPlayCount(music.getPlayCount());
|
||||
dto.setCreateTime(music.getCreateTime());
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.test.musichouduan.dto.admin;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 统计项数据传输对象
|
||||
*/
|
||||
@Data
|
||||
public class StatItemDTO {
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
private long value;
|
||||
|
||||
/**
|
||||
* 子项列表
|
||||
*/
|
||||
private List<StatItemDTO> children;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.test.musichouduan.dto.admin;
|
||||
|
||||
import com.test.musichouduan.entity.User;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户管理数据传输对象
|
||||
*/
|
||||
@Data
|
||||
public class UserAdminDTO {
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 头像URL
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 性别:0-未知,1-男,2-女
|
||||
*/
|
||||
private Integer gender;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 角色:0-普通用户,1-管理员
|
||||
*/
|
||||
private Integer role;
|
||||
|
||||
/**
|
||||
* 状态:0-禁用,1-正常
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 将实体转换为DTO
|
||||
*
|
||||
* @param user 用户实体
|
||||
* @return 用户DTO
|
||||
*/
|
||||
public static UserAdminDTO fromEntity(User user) {
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
UserAdminDTO dto = new UserAdminDTO();
|
||||
dto.setId(user.getId());
|
||||
dto.setUsername(user.getUsername());
|
||||
dto.setNickname(user.getNickname());
|
||||
dto.setAvatar(user.getAvatar());
|
||||
dto.setGender(user.getGender());
|
||||
dto.setEmail(user.getEmail());
|
||||
dto.setPhone(user.getPhone());
|
||||
dto.setRole(user.getRole());
|
||||
dto.setStatus(user.getStatus());
|
||||
dto.setCreateTime(user.getCreateTime());
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.test.musichouduan.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 轮播图实体类
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "banner")
|
||||
public class Banner {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
@Column(length = 100)
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 图片URL
|
||||
*/
|
||||
@Column(nullable = false, length = 255)
|
||||
private String imageUrl;
|
||||
|
||||
/**
|
||||
* 链接URL
|
||||
*/
|
||||
@Column(length = 255)
|
||||
private String linkUrl;
|
||||
|
||||
/**
|
||||
* 目标ID
|
||||
*/
|
||||
@Column
|
||||
private Long targetId;
|
||||
|
||||
/**
|
||||
* 目标类型:1-音乐,2-歌单,3-歌手
|
||||
*/
|
||||
@Column
|
||||
private Integer targetType;
|
||||
|
||||
/**
|
||||
* 排序号
|
||||
*/
|
||||
@Column
|
||||
private Integer orderNum;
|
||||
|
||||
/**
|
||||
* 状态:0-禁用,1-正常
|
||||
*/
|
||||
@Column
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@CreationTimestamp
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@UpdateTimestamp
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.test.musichouduan.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 评论实体类
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "comment")
|
||||
public class Comment {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 评论内容
|
||||
*/
|
||||
@Column(nullable = false, length = 500)
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 评论用户
|
||||
*/
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
private User user;
|
||||
|
||||
/**
|
||||
* 评论目标ID
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private Long targetId;
|
||||
|
||||
/**
|
||||
* 评论目标类型:1-音乐,2-歌单,3-歌手
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private Integer targetType;
|
||||
|
||||
/**
|
||||
* 父评论ID
|
||||
*/
|
||||
@Column
|
||||
private Long parentId;
|
||||
|
||||
/**
|
||||
* 状态:0-禁用,1-正常
|
||||
*/
|
||||
@Column
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@CreationTimestamp
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@UpdateTimestamp
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.test.musichouduan.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 音乐实体类
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "music")
|
||||
public class Music {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 音乐名称
|
||||
*/
|
||||
@Column(nullable = false, length = 100)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 封面URL
|
||||
*/
|
||||
@Column(length = 255)
|
||||
private String cover;
|
||||
|
||||
/**
|
||||
* 音乐文件URL
|
||||
*/
|
||||
@Column(nullable = false, length = 255)
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 歌词
|
||||
*/
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String lyric;
|
||||
|
||||
/**
|
||||
* 时长(秒)
|
||||
*/
|
||||
@Column
|
||||
private Integer duration;
|
||||
|
||||
/**
|
||||
* 专辑
|
||||
*/
|
||||
@Column(length = 100)
|
||||
private String album;
|
||||
|
||||
/**
|
||||
* 播放次数
|
||||
*/
|
||||
@Column
|
||||
private Long playCount;
|
||||
|
||||
/**
|
||||
* 歌手列表
|
||||
*/
|
||||
@ManyToMany(mappedBy = "musics")
|
||||
private Set<Singer> singers = new HashSet<>();
|
||||
|
||||
/**
|
||||
* 音乐类型列表
|
||||
*/
|
||||
@ManyToMany
|
||||
@JoinTable(
|
||||
name = "music_type_relation",
|
||||
joinColumns = @JoinColumn(name = "music_id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "type_id")
|
||||
)
|
||||
private Set<MusicType> types = new HashSet<>();
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@CreationTimestamp
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@UpdateTimestamp
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.test.musichouduan.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 音乐类型实体类
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "music_type")
|
||||
public class MusicType {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 类型名称
|
||||
*/
|
||||
@Column(nullable = false, unique = true, length = 50)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 类型描述
|
||||
*/
|
||||
@Column(length = 255)
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@CreationTimestamp
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@UpdateTimestamp
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.test.musichouduan.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 播放历史实体类
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "play_history")
|
||||
public class PlayHistory {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户
|
||||
*/
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
private User user;
|
||||
|
||||
/**
|
||||
* 音乐
|
||||
*/
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "music_id", nullable = false)
|
||||
private Music music;
|
||||
|
||||
/**
|
||||
* 播放时间
|
||||
*/
|
||||
@CreationTimestamp
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime playTime;
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.test.musichouduan.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 歌单实体类
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "playlist")
|
||||
public class Playlist {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 歌单名称
|
||||
*/
|
||||
@Column(nullable = false, length = 100)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 封面URL
|
||||
*/
|
||||
@Column(length = 255)
|
||||
private String cover;
|
||||
|
||||
/**
|
||||
* 歌单描述
|
||||
*/
|
||||
@Column(length = 500)
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "creator_id")
|
||||
private User creator;
|
||||
|
||||
/**
|
||||
* 歌单类型
|
||||
*/
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "type_id")
|
||||
private PlaylistType type;
|
||||
|
||||
/**
|
||||
* 是否官方歌单:0-否,1-是
|
||||
*/
|
||||
@Column
|
||||
private Integer isOfficial;
|
||||
|
||||
/**
|
||||
* 播放次数
|
||||
*/
|
||||
@Column
|
||||
private Long playCount;
|
||||
|
||||
/**
|
||||
* 歌单中的音乐
|
||||
*/
|
||||
@ManyToMany
|
||||
@JoinTable(
|
||||
name = "playlist_music",
|
||||
joinColumns = @JoinColumn(name = "playlist_id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "music_id")
|
||||
)
|
||||
private Set<Music> musics = new HashSet<>();
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@CreationTimestamp
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@UpdateTimestamp
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.test.musichouduan.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 歌单类型实体类
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "playlist_type")
|
||||
public class PlaylistType {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 类型名称
|
||||
*/
|
||||
@Column(nullable = false, unique = true, length = 50)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 类型描述
|
||||
*/
|
||||
@Column(length = 255)
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@CreationTimestamp
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@UpdateTimestamp
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.test.musichouduan.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* 歌手实体类
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "singer")
|
||||
@EqualsAndHashCode(exclude = {"musics"})
|
||||
public class Singer {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 歌手名称
|
||||
*/
|
||||
@Column(nullable = false, length = 100)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 歌手头像URL
|
||||
*/
|
||||
@Column(length = 255)
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 歌手简介
|
||||
*/
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String introduction;
|
||||
|
||||
/**
|
||||
* 歌手类型ID
|
||||
*/
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "type_id")
|
||||
private SingerType type;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@CreationTimestamp
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@UpdateTimestamp
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 歌手关联的音乐
|
||||
*/
|
||||
@ManyToMany
|
||||
@JoinTable(
|
||||
name = "singer_music",
|
||||
joinColumns = @JoinColumn(name = "singer_id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "music_id")
|
||||
)
|
||||
private Set<Music> musics = new HashSet<>();
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.test.musichouduan.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 歌手类型实体类
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "singer_type")
|
||||
public class SingerType {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 类型名称
|
||||
*/
|
||||
@Column(nullable = false, unique = true, length = 50)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 类型描述
|
||||
*/
|
||||
@Column(length = 255)
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@CreationTimestamp
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@UpdateTimestamp
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.test.musichouduan.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户实体类
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "user")
|
||||
public class User {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@Column(nullable = false, unique = true, length = 50)
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
@Column(nullable = false, length = 100)
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
@Column(length = 50)
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 头像URL
|
||||
*/
|
||||
@Column(length = 255)
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 性别:0-未知,1-男,2-女
|
||||
*/
|
||||
@Column
|
||||
private Integer gender;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
@Column(length = 100)
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
@Column(length = 20)
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 个人简介
|
||||
*/
|
||||
@Column(length = 500)
|
||||
private String introduction;
|
||||
|
||||
/**
|
||||
* 角色:0-普通用户,1-管理员
|
||||
*/
|
||||
@Column
|
||||
private Integer role;
|
||||
|
||||
/**
|
||||
* 状态:0-禁用,1-正常
|
||||
*/
|
||||
@Column
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@CreationTimestamp
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@UpdateTimestamp
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.test.musichouduan.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户收藏音乐实体类
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "user_collect_music")
|
||||
public class UserCollectMusic {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户
|
||||
*/
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
private User user;
|
||||
|
||||
/**
|
||||
* 音乐
|
||||
*/
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "music_id", nullable = false)
|
||||
private Music music;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@CreationTimestamp
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.test.musichouduan.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户收藏歌单实体类
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "user_collect_playlist")
|
||||
public class UserCollectPlaylist {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户
|
||||
*/
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
private User user;
|
||||
|
||||
/**
|
||||
* 歌单
|
||||
*/
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "playlist_id", nullable = false)
|
||||
private Playlist playlist;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@CreationTimestamp
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.test.musichouduan.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户收藏歌手实体类
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "user_collect_singer")
|
||||
public class UserCollectSinger {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户
|
||||
*/
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
private User user;
|
||||
|
||||
/**
|
||||
* 歌手
|
||||
*/
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "singer_id", nullable = false)
|
||||
private Singer singer;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@CreationTimestamp
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.test.musichouduan.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户音乐关联实体
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "user_music")
|
||||
public class UserMusic {
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户
|
||||
*/
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "user_id")
|
||||
private User user;
|
||||
|
||||
/**
|
||||
* 音乐
|
||||
*/
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "music_id")
|
||||
private Music music;
|
||||
|
||||
/**
|
||||
* 收藏时间
|
||||
*/
|
||||
@CreationTimestamp
|
||||
@Column(name = "collect_time", updatable = false)
|
||||
private LocalDateTime collectTime;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.test.musichouduan.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户歌单关联实体
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "user_playlist")
|
||||
public class UserPlaylist {
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户
|
||||
*/
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "user_id")
|
||||
private User user;
|
||||
|
||||
/**
|
||||
* 歌单
|
||||
*/
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "playlist_id")
|
||||
private Playlist playlist;
|
||||
|
||||
/**
|
||||
* 收藏时间
|
||||
*/
|
||||
@CreationTimestamp
|
||||
@Column(name = "collect_time", updatable = false)
|
||||
private LocalDateTime collectTime;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.test.musichouduan.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户歌手关联实体
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "user_singer")
|
||||
public class UserSinger {
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户
|
||||
*/
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "user_id")
|
||||
private User user;
|
||||
|
||||
/**
|
||||
* 歌手
|
||||
*/
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "singer_id")
|
||||
private Singer singer;
|
||||
|
||||
/**
|
||||
* 收藏时间
|
||||
*/
|
||||
@CreationTimestamp
|
||||
@Column(name = "collect_time", updatable = false)
|
||||
private LocalDateTime collectTime;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.test.musichouduan.exception;
|
||||
|
||||
/**
|
||||
* 业务异常
|
||||
*/
|
||||
public class BusinessException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
private Integer code;
|
||||
|
||||
/**
|
||||
* 错误消息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
*
|
||||
* @param message 错误消息
|
||||
*/
|
||||
public BusinessException(String message) {
|
||||
this.code = 500;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
*
|
||||
* @param code 错误码
|
||||
* @param message 错误消息
|
||||
*/
|
||||
public BusinessException(Integer code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误码
|
||||
*
|
||||
* @return 错误码
|
||||
*/
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误消息
|
||||
*
|
||||
* @return 错误消息
|
||||
*/
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.test.musichouduan.exception;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
import com.test.musichouduan.dto.Result;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
||||
import org.springframework.web.servlet.resource.NoResourceFoundException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 全局异常处理器
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||
|
||||
/**
|
||||
* 处理自定义异常
|
||||
*/
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public Result<Void> handleBusinessException(BusinessException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return Result.error(e.getCode(), e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理未登录异常
|
||||
*/
|
||||
@ExceptionHandler(NotLoginException.class)
|
||||
public Result<Void> handleNotLoginException(NotLoginException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return Result.error(401, "未登录或登录已过期,请重新登录");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理无权限异常
|
||||
*/
|
||||
@ExceptionHandler(NotPermissionException.class)
|
||||
public Result<Void> handleNotPermissionException(NotPermissionException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return Result.error(403, "无权限访问");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理无角色异常
|
||||
*/
|
||||
@ExceptionHandler(NotRoleException.class)
|
||||
public Result<Void> handleNotRoleException(NotRoleException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return Result.error(403, "无权限访问");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理参数校验异常
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public Result<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
BindingResult bindingResult = e.getBindingResult();
|
||||
StringBuilder sb = new StringBuilder("参数校验失败:");
|
||||
for (FieldError fieldError : bindingResult.getFieldErrors()) {
|
||||
sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", ");
|
||||
}
|
||||
String msg = sb.toString();
|
||||
if (msg.endsWith(", ")) {
|
||||
msg = msg.substring(0, msg.length() - 2);
|
||||
}
|
||||
return Result.error(400, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理参数绑定异常
|
||||
*/
|
||||
@ExceptionHandler(BindException.class)
|
||||
public Result<Void> handleBindException(BindException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
BindingResult bindingResult = e.getBindingResult();
|
||||
StringBuilder sb = new StringBuilder("参数绑定失败:");
|
||||
for (FieldError fieldError : bindingResult.getFieldErrors()) {
|
||||
sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", ");
|
||||
}
|
||||
String msg = sb.toString();
|
||||
if (msg.endsWith(", ")) {
|
||||
msg = msg.substring(0, msg.length() - 2);
|
||||
}
|
||||
return Result.error(400, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理文件上传大小超限异常
|
||||
*/
|
||||
@ExceptionHandler(MaxUploadSizeExceededException.class)
|
||||
public Result<Void> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return Result.error(400, "上传文件大小超出限制");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理资源未找到异常
|
||||
*/
|
||||
@ExceptionHandler(NoResourceFoundException.class)
|
||||
public Result<Void> handleNoResourceFoundException(NoResourceFoundException e, HttpServletRequest request) {
|
||||
// 如果是 favicon.ico 的请求,则不记录日志
|
||||
String requestURI = request.getRequestURI();
|
||||
if (requestURI.endsWith("/favicon.ico")) {
|
||||
return Result.error(404, "Resource not found");
|
||||
}
|
||||
|
||||
// 其他资源未找到的情况,记录日志
|
||||
logger.error(e.getMessage(), e);
|
||||
return Result.error(404, "Resource not found");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理其他异常
|
||||
*/
|
||||
@ExceptionHandler(Exception.class)
|
||||
public Result<Void> handleException(Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return Result.error(500, "服务器内部错误");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.test.musichouduan.init;
|
||||
|
||||
import cn.dev33.satoken.secure.BCrypt;
|
||||
import com.test.musichouduan.entity.*;
|
||||
import com.test.musichouduan.repository.*;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据初始化器
|
||||
*/
|
||||
@Component
|
||||
public class DataInitializer implements CommandLineRunner {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
private MusicTypeRepository musicTypeRepository;
|
||||
|
||||
@Autowired
|
||||
private SingerTypeRepository singerTypeRepository;
|
||||
|
||||
@Autowired
|
||||
private PlaylistTypeRepository playlistTypeRepository;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void run(String... args) throws Exception {
|
||||
// 初始化管理员用户
|
||||
initAdminUser();
|
||||
|
||||
// 初始化音乐类型
|
||||
initMusicTypes();
|
||||
|
||||
// 初始化歌手类型
|
||||
initSingerTypes();
|
||||
|
||||
// 初始化歌单类型
|
||||
initPlaylistTypes();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化管理员用户
|
||||
*/
|
||||
private void initAdminUser() {
|
||||
if (!userRepository.existsByUsername("admin")) {
|
||||
User admin = new User();
|
||||
admin.setUsername("admin");
|
||||
admin.setPassword(BCrypt.hashpw("123456"));
|
||||
admin.setNickname("管理员");
|
||||
admin.setGender(0);
|
||||
admin.setRole(1);
|
||||
admin.setStatus(1);
|
||||
userRepository.save(admin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化音乐类型
|
||||
*/
|
||||
private void initMusicTypes() {
|
||||
if (musicTypeRepository.count() == 0) {
|
||||
List<String> typeNames = Arrays.asList("流行", "摇滚", "民谣", "电子", "古典", "嘻哈", "爵士", "乡村");
|
||||
for (String name : typeNames) {
|
||||
MusicType type = new MusicType();
|
||||
type.setName(name);
|
||||
type.setDescription(name + "音乐");
|
||||
musicTypeRepository.save(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化歌手类型
|
||||
*/
|
||||
private void initSingerTypes() {
|
||||
if (singerTypeRepository.count() == 0) {
|
||||
List<String> typeNames = Arrays.asList("华语男歌手", "华语女歌手", "华语组合", "欧美男歌手", "欧美女歌手", "欧美组合", "日韩男歌手", "日韩女歌手", "日韩组合");
|
||||
for (String name : typeNames) {
|
||||
SingerType type = new SingerType();
|
||||
type.setName(name);
|
||||
type.setDescription(name);
|
||||
singerTypeRepository.save(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化歌单类型
|
||||
*/
|
||||
private void initPlaylistTypes() {
|
||||
if (playlistTypeRepository.count() == 0) {
|
||||
List<String> typeNames = Arrays.asList("流行", "摇滚", "民谣", "电子", "古典", "嘻哈", "爵士", "乡村", "轻音乐", "ACG");
|
||||
for (String name : typeNames) {
|
||||
PlaylistType type = new PlaylistType();
|
||||
type.setName(name);
|
||||
type.setDescription(name + "歌单");
|
||||
playlistTypeRepository.save(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.test.musichouduan.repository;
|
||||
|
||||
import com.test.musichouduan.entity.Banner;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 轮播图数据访问层
|
||||
*/
|
||||
@Repository
|
||||
public interface BannerRepository extends JpaRepository<Banner, Long> {
|
||||
|
||||
/**
|
||||
* 根据状态查询轮播图
|
||||
*
|
||||
* @param status 状态
|
||||
* @return 轮播图列表
|
||||
*/
|
||||
List<Banner> findByStatusOrderByOrderNumAsc(Integer status);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.test.musichouduan.repository;
|
||||
|
||||
import com.test.musichouduan.entity.Comment;
|
||||
import com.test.musichouduan.entity.User;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 评论数据访问层
|
||||
*/
|
||||
@Repository
|
||||
public interface CommentRepository extends JpaRepository<Comment, Long>, JpaSpecificationExecutor<Comment> {
|
||||
|
||||
/**
|
||||
* 根据目标ID和目标类型查询评论
|
||||
*
|
||||
* @param targetId 目标ID
|
||||
* @param targetType 目标类型
|
||||
* @param pageable 分页
|
||||
* @return 评论列表
|
||||
*/
|
||||
Page<Comment> findByTargetIdAndTargetTypeAndParentIdIsNullOrderByCreateTimeDesc(Long targetId, Integer targetType, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 根据父评论ID查询子评论
|
||||
*
|
||||
* @param parentId 父评论ID
|
||||
* @return 子评论列表
|
||||
*/
|
||||
List<Comment> findByParentIdOrderByCreateTimeAsc(Long parentId);
|
||||
|
||||
/**
|
||||
* 根据用户查询评论
|
||||
*
|
||||
* @param user 用户
|
||||
* @param pageable 分页
|
||||
* @return 评论列表
|
||||
*/
|
||||
Page<Comment> findByUserOrderByCreateTimeDesc(User user, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 根据目标ID和目标类型统计评论数量
|
||||
*
|
||||
* @param targetId 目标ID
|
||||
* @param targetType 目标类型
|
||||
* @return 评论数量
|
||||
*/
|
||||
long countByTargetIdAndTargetType(Long targetId, Integer targetType);
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.test.musichouduan.repository;
|
||||
|
||||
import com.test.musichouduan.entity.Music;
|
||||
import com.test.musichouduan.entity.MusicType;
|
||||
import com.test.musichouduan.entity.Singer;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 音乐数据访问层
|
||||
*/
|
||||
@Repository
|
||||
public interface MusicRepository extends JpaRepository<Music, Long>, JpaSpecificationExecutor<Music> {
|
||||
|
||||
/**
|
||||
* 根据名称模糊查询音乐
|
||||
*
|
||||
* @param name 名称
|
||||
* @param pageable 分页
|
||||
* @return 音乐列表
|
||||
*/
|
||||
Page<Music> findByNameContaining(String name, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 根据类型查询音乐
|
||||
*
|
||||
* @param type 类型
|
||||
* @param pageable 分页
|
||||
* @return 音乐列表
|
||||
*/
|
||||
@Query("SELECT m FROM Music m JOIN m.types t WHERE t = :type")
|
||||
Page<Music> findByType(MusicType type, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 根据歌手查询音乐
|
||||
*
|
||||
* @param singer 歌手
|
||||
* @param pageable 分页
|
||||
* @return 音乐列表
|
||||
*/
|
||||
@Query("SELECT m FROM Music m JOIN m.singers s WHERE s = :singer")
|
||||
Page<Music> findBySinger(Singer singer, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 根据歌手查询音乐
|
||||
*
|
||||
* @param singer 歌手
|
||||
* @param pageable 分页
|
||||
* @return 音乐列表
|
||||
*/
|
||||
Page<Music> findBySingersContaining(Singer singer, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 统计歌手的音乐数量
|
||||
*
|
||||
* @param singer 歌手
|
||||
* @return 音乐数量
|
||||
*/
|
||||
Long countBySingersContaining(Singer singer);
|
||||
|
||||
/**
|
||||
* 检查是否存在指定类型的音乐
|
||||
*
|
||||
* @param type 音乐类型
|
||||
* @return 是否存在
|
||||
*/
|
||||
boolean existsByTypesContaining(MusicType type);
|
||||
|
||||
/**
|
||||
* 查询最新的音乐
|
||||
*
|
||||
* @param pageable 分页
|
||||
* @return 音乐列表
|
||||
*/
|
||||
@Query("SELECT m FROM Music m ORDER BY m.createTime DESC")
|
||||
List<Music> findLatest(Pageable pageable);
|
||||
|
||||
/**
|
||||
* 查询播放次数最多的音乐
|
||||
*
|
||||
* @param pageable 分页
|
||||
* @return 音乐列表
|
||||
*/
|
||||
@Query("SELECT m FROM Music m ORDER BY m.playCount DESC")
|
||||
List<Music> findMostPlayed(Pageable pageable);
|
||||
|
||||
/**
|
||||
* 统计包含指定类型的音乐数量
|
||||
*
|
||||
* @param type 音乐类型
|
||||
* @return 音乐数量
|
||||
*/
|
||||
long countByTypesContaining(MusicType type);
|
||||
|
||||
/**
|
||||
* 统计所有音乐的总播放量
|
||||
*
|
||||
* @return 总播放量
|
||||
*/
|
||||
@Query("SELECT COALESCE(SUM(m.playCount), 0) FROM Music m")
|
||||
long sumPlayCount();
|
||||
|
||||
/**
|
||||
* 统计各个音乐类型的音乐数量
|
||||
*
|
||||
* @return 音乐类型及其对应的音乐数量
|
||||
*/
|
||||
@Query("SELECT t.name, COUNT(m) FROM Music m JOIN m.types t GROUP BY t.name ORDER BY COUNT(m) DESC")
|
||||
List<Object[]> countByMusicType();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.test.musichouduan.repository;
|
||||
|
||||
import com.test.musichouduan.entity.MusicType;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 音乐类型数据访问层
|
||||
*/
|
||||
@Repository
|
||||
public interface MusicTypeRepository extends JpaRepository<MusicType, Long>, JpaSpecificationExecutor<MusicType> {
|
||||
|
||||
/**
|
||||
* 根据名称查找音乐类型
|
||||
*
|
||||
* @param name 名称
|
||||
* @return 音乐类型
|
||||
*/
|
||||
Optional<MusicType> findByName(String name);
|
||||
|
||||
/**
|
||||
* 判断名称是否存在
|
||||
*
|
||||
* @param name 名称
|
||||
* @return 是否存在
|
||||
*/
|
||||
boolean existsByName(String name);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.test.musichouduan.repository;
|
||||
|
||||
import com.test.musichouduan.entity.Music;
|
||||
import com.test.musichouduan.entity.PlayHistory;
|
||||
import com.test.musichouduan.entity.User;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 播放历史数据访问层
|
||||
*/
|
||||
@Repository
|
||||
public interface PlayHistoryRepository extends JpaRepository<PlayHistory, Long> {
|
||||
|
||||
/**
|
||||
/**
|
||||
/**
|
||||
* 根据用户查询播放历史
|
||||
*
|
||||
* @param user 用户
|
||||
* @param pageable 分页
|
||||
* @return 播放历史列表
|
||||
*/
|
||||
Page<PlayHistory> findByUserOrderByPlayTimeDesc(User user, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 根据用户和音乐查询播放历史
|
||||
*
|
||||
* @param user 用户
|
||||
* @param music 音乐
|
||||
* @return 播放历史
|
||||
*/
|
||||
Optional<PlayHistory> findByUserAndMusic(User user, Music music);
|
||||
|
||||
/**
|
||||
* 根据用户统计播放历史数量
|
||||
*
|
||||
* @param user 用户
|
||||
* @return 播放历史数量
|
||||
*/
|
||||
long countByUser(User user);
|
||||
|
||||
/**
|
||||
* 根据音乐统计播放次数
|
||||
*
|
||||
* @param music 音乐
|
||||
* @return 播放次数
|
||||
*/
|
||||
long countByMusic(Music music);
|
||||
|
||||
/**
|
||||
* 统计指定时间范围内的播放次数
|
||||
*
|
||||
* @param start 开始时间
|
||||
* @param end 结束时间
|
||||
* @return 播放次数
|
||||
*/
|
||||
|
||||
long countByPlayTimeBetween(LocalDateTime start, LocalDateTime end);
|
||||
|
||||
/**
|
||||
*记录播放历史
|
||||
* @param userId 用户ID
|
||||
* @param musicId 音乐ID
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query(value = "INSERT INTO play_history (user_id, music_id, play_time) VALUES (?1, ?2, NOW()) " +
|
||||
"ON DUPLICATE KEY UPDATE play_time = NOW()", nativeQuery = true)
|
||||
void recordPlayHistory(Long userId, Long musicId);
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.test.musichouduan.repository;
|
||||
|
||||
import com.test.musichouduan.entity.Playlist;
|
||||
import com.test.musichouduan.entity.PlaylistType;
|
||||
import com.test.musichouduan.entity.User;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 歌单数据访问层
|
||||
*/
|
||||
@Repository
|
||||
public interface PlaylistRepository extends JpaRepository<Playlist, Long>, JpaSpecificationExecutor<Playlist> {
|
||||
|
||||
@Query("SELECT pt.name, COUNT(p) FROM Playlist p JOIN p.type pt GROUP BY pt.name")
|
||||
List<Object[]> countByPlaylistType();
|
||||
|
||||
/**
|
||||
* 根据类型统计歌单数量
|
||||
*
|
||||
* @param type 歌单类型
|
||||
* @return 歌单数量
|
||||
*/
|
||||
long countByType(PlaylistType type);
|
||||
|
||||
/**
|
||||
* 根据名称模糊查询歌单
|
||||
*
|
||||
* @param name 名称
|
||||
* @param pageable 分页
|
||||
* @return 歌单列表
|
||||
*/
|
||||
Page<Playlist> findByNameContaining(String name, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 根据类型查询歌单
|
||||
*
|
||||
* @param type 类型
|
||||
* @param pageable 分页
|
||||
* @return 歌单列表
|
||||
*/
|
||||
Page<Playlist> findByType(PlaylistType type, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 根据创建者查询歌单
|
||||
*
|
||||
* @param creator 创建者
|
||||
* @param pageable 分页
|
||||
* @return 歌单列表
|
||||
*/
|
||||
Page<Playlist> findByCreator(User creator, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 查询官方歌单
|
||||
*
|
||||
* @param pageable 分页
|
||||
* @return 歌单列表
|
||||
*/
|
||||
Page<Playlist> findByIsOfficial(Integer isOfficial, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 查询最新的歌单
|
||||
*
|
||||
* @param pageable 分页
|
||||
* @return 歌单列表
|
||||
*/
|
||||
@Query("SELECT p FROM Playlist p ORDER BY p.createTime DESC")
|
||||
List<Playlist> findLatest(Pageable pageable);
|
||||
|
||||
/**
|
||||
* 查询播放次数最多的歌单
|
||||
*
|
||||
* @param pageable 分页
|
||||
* @return 歌单列表
|
||||
*/
|
||||
@Query("SELECT p FROM Playlist p ORDER BY p.playCount DESC")
|
||||
List<Playlist> findMostPlayed(Pageable pageable);
|
||||
|
||||
/**
|
||||
* 统计用户创建的歌单数量
|
||||
*
|
||||
* @param creator 创建者
|
||||
* @return 歌单数量
|
||||
*/
|
||||
long countByCreator(User creator);
|
||||
|
||||
/**
|
||||
* 检查是否存在指定类型的歌单
|
||||
*
|
||||
* @param type 歌单类型
|
||||
* @return 是否存在
|
||||
*/
|
||||
boolean existsByType(PlaylistType type);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.test.musichouduan.repository;
|
||||
|
||||
import com.test.musichouduan.entity.PlaylistType;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 歌单类型数据访问层
|
||||
*/
|
||||
@Repository
|
||||
public interface PlaylistTypeRepository extends JpaRepository<PlaylistType, Long>, JpaSpecificationExecutor<PlaylistType> {
|
||||
|
||||
/**
|
||||
* 根据名称查找歌单类型
|
||||
*
|
||||
* @param name 名称
|
||||
* @return 歌单类型
|
||||
*/
|
||||
Optional<PlaylistType> findByName(String name);
|
||||
|
||||
/**
|
||||
* 判断名称是否存在
|
||||
*
|
||||
* @param name 名称
|
||||
* @return 是否存在
|
||||
*/
|
||||
boolean existsByName(String name);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.test.musichouduan.repository;
|
||||
|
||||
import com.test.musichouduan.entity.Singer;
|
||||
import com.test.musichouduan.entity.SingerType;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 歌手数据访问层
|
||||
*/
|
||||
@Repository
|
||||
public interface SingerRepository extends JpaRepository<Singer, Long>, JpaSpecificationExecutor<Singer> {
|
||||
|
||||
@Query("SELECT st.name, COUNT(s) FROM Singer s JOIN s.type st GROUP BY st.name")
|
||||
List<Object[]> countBySingerType();
|
||||
|
||||
/**
|
||||
* 根据类型统计歌手数量
|
||||
*
|
||||
* @param type 歌手类型
|
||||
* @return 歌手数量
|
||||
*/
|
||||
long countByType(SingerType type);
|
||||
|
||||
/**
|
||||
* 根据名称查找歌手
|
||||
*
|
||||
* @param name 名称
|
||||
* @return 歌手
|
||||
*/
|
||||
Optional<Singer> findByName(String name);
|
||||
|
||||
/**
|
||||
* 根据类型查找歌手
|
||||
*
|
||||
* @param type 类型
|
||||
* @param pageable 分页
|
||||
* @return 歌手列表
|
||||
*/
|
||||
Page<Singer> findByType(SingerType type, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 根据名称模糊查询歌手
|
||||
*
|
||||
* @param name 名称
|
||||
* @param pageable 分页
|
||||
* @return 歌手列表
|
||||
*/
|
||||
Page<Singer> findByNameContaining(String name, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 查询最新的歌手
|
||||
*
|
||||
* @param limit 限制数量
|
||||
* @return 歌手列表
|
||||
*/
|
||||
@Query(value = "SELECT s FROM Singer s ORDER BY s.createTime DESC")
|
||||
List<Singer> findLatest(Pageable pageable);
|
||||
|
||||
/**
|
||||
* 查询热门歌手(按照音乐数量排序)
|
||||
*
|
||||
* @param pageable 分页
|
||||
* @return 歌手列表
|
||||
*/
|
||||
@Query(value = "SELECT s.* FROM singer s LEFT JOIN (SELECT singer_id, COUNT(*) as music_count FROM music_singer GROUP BY singer_id) ms ON s.id = ms.singer_id ORDER BY COALESCE(ms.music_count, 0) DESC", nativeQuery = true)
|
||||
List<Singer> findHotSingers(Pageable pageable);
|
||||
|
||||
/**
|
||||
* 检查是否存在指定类型的歌手
|
||||
*
|
||||
* @param type 歌手类型
|
||||
* @return 是否存在
|
||||
*/
|
||||
boolean existsByType(SingerType type);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.test.musichouduan.repository;
|
||||
|
||||
import com.test.musichouduan.entity.SingerType;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 歌手类型数据访问层
|
||||
*/
|
||||
@Repository
|
||||
public interface SingerTypeRepository extends JpaRepository<SingerType, Long>, JpaSpecificationExecutor<SingerType> {
|
||||
|
||||
/**
|
||||
* 根据名称查找歌手类型
|
||||
*
|
||||
* @param name 名称
|
||||
* @return 歌手类型
|
||||
*/
|
||||
Optional<SingerType> findByName(String name);
|
||||
|
||||
/**
|
||||
* 判断名称是否存在
|
||||
*
|
||||
* @param name 名称
|
||||
* @return 是否存在
|
||||
*/
|
||||
boolean existsByName(String name);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.test.musichouduan.repository;
|
||||
|
||||
import com.test.musichouduan.entity.Music;
|
||||
import com.test.musichouduan.entity.User;
|
||||
import com.test.musichouduan.entity.UserCollectMusic;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 用户收藏音乐数据访问层
|
||||
*/
|
||||
@Repository
|
||||
public interface UserCollectMusicRepository extends JpaRepository<UserCollectMusic, Long> {
|
||||
|
||||
/**
|
||||
* 根据用户查询收藏的音乐
|
||||
*
|
||||
* @param user 用户
|
||||
* @param pageable 分页
|
||||
* @return 收藏的音乐列表
|
||||
*/
|
||||
Page<UserCollectMusic> findByUser(User user, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 根据用户和音乐查询收藏记录
|
||||
*
|
||||
* @param user 用户
|
||||
* @param music 音乐
|
||||
* @return 收藏记录
|
||||
*/
|
||||
Optional<UserCollectMusic> findByUserAndMusic(User user, Music music);
|
||||
|
||||
/**
|
||||
* 判断用户是否收藏了音乐
|
||||
*
|
||||
* @param user 用户
|
||||
* @param music 音乐
|
||||
* @return 是否收藏
|
||||
*/
|
||||
boolean existsByUserAndMusic(User user, Music music);
|
||||
|
||||
/**
|
||||
* 根据用户统计收藏的音乐数量
|
||||
*
|
||||
* @param user 用户
|
||||
* @return 收藏的音乐数量
|
||||
*/
|
||||
long countByUser(User user);
|
||||
|
||||
/**
|
||||
* 根据音乐统计被收藏的次数
|
||||
*
|
||||
* @param music 音乐
|
||||
* @return 被收藏的次数
|
||||
*/
|
||||
long countByMusic(Music music);
|
||||
|
||||
/**
|
||||
* 根据用户和音乐删除收藏记录
|
||||
*
|
||||
* @param user 用户
|
||||
* @param music 音乐
|
||||
*/
|
||||
void deleteByUserAndMusic(User user, Music music);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.test.musichouduan.repository;
|
||||
|
||||
import com.test.musichouduan.entity.Playlist;
|
||||
import com.test.musichouduan.entity.User;
|
||||
import com.test.musichouduan.entity.UserCollectPlaylist;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 用户收藏歌单数据访问层
|
||||
*/
|
||||
@Repository
|
||||
public interface UserCollectPlaylistRepository extends JpaRepository<UserCollectPlaylist, Long> {
|
||||
|
||||
/**
|
||||
* 根据用户查询收藏的歌单
|
||||
*
|
||||
* @param user 用户
|
||||
* @param pageable 分页
|
||||
* @return 收藏的歌单列表
|
||||
*/
|
||||
Page<UserCollectPlaylist> findByUser(User user, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 根据用户和歌单查询收藏记录
|
||||
*
|
||||
* @param user 用户
|
||||
* @param playlist 歌单
|
||||
* @return 收藏记录
|
||||
*/
|
||||
Optional<UserCollectPlaylist> findByUserAndPlaylist(User user, Playlist playlist);
|
||||
|
||||
/**
|
||||
* 判断用户是否收藏了歌单
|
||||
*
|
||||
* @param user 用户
|
||||
* @param playlist 歌单
|
||||
* @return 是否收藏
|
||||
*/
|
||||
boolean existsByUserAndPlaylist(User user, Playlist playlist);
|
||||
|
||||
/**
|
||||
* 根据用户统计收藏的歌单数量
|
||||
*
|
||||
* @param user 用户
|
||||
* @return 收藏的歌单数量
|
||||
*/
|
||||
long countByUser(User user);
|
||||
|
||||
/**
|
||||
* 根据歌单统计被收藏的次数
|
||||
*
|
||||
* @param playlist 歌单
|
||||
* @return 被收藏的次数
|
||||
*/
|
||||
long countByPlaylist(Playlist playlist);
|
||||
|
||||
/**
|
||||
* 根据用户和歌单删除收藏记录
|
||||
*
|
||||
* @param user 用户
|
||||
* @param playlist 歌单
|
||||
*/
|
||||
void deleteByUserAndPlaylist(User user, Playlist playlist);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.test.musichouduan.repository;
|
||||
|
||||
import com.test.musichouduan.entity.Singer;
|
||||
import com.test.musichouduan.entity.User;
|
||||
import com.test.musichouduan.entity.UserCollectSinger;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 用户收藏歌手数据访问层
|
||||
*/
|
||||
@Repository
|
||||
public interface UserCollectSingerRepository extends JpaRepository<UserCollectSinger, Long> {
|
||||
|
||||
/**
|
||||
* 根据用户查询收藏的歌手
|
||||
*
|
||||
* @param user 用户
|
||||
* @param pageable 分页
|
||||
* @return 收藏的歌手列表
|
||||
*/
|
||||
Page<UserCollectSinger> findByUser(User user, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 根据用户和歌手查询收藏记录
|
||||
*
|
||||
* @param user 用户
|
||||
* @param singer 歌手
|
||||
* @return 收藏记录
|
||||
*/
|
||||
Optional<UserCollectSinger> findByUserAndSinger(User user, Singer singer);
|
||||
|
||||
/**
|
||||
* 判断用户是否收藏了歌手
|
||||
*
|
||||
* @param user 用户
|
||||
* @param singer 歌手
|
||||
* @return 是否收藏
|
||||
*/
|
||||
boolean existsByUserAndSinger(User user, Singer singer);
|
||||
|
||||
/**
|
||||
* 根据用户统计收藏的歌手数量
|
||||
*
|
||||
* @param user 用户
|
||||
* @return 收藏的歌手数量
|
||||
*/
|
||||
long countByUser(User user);
|
||||
|
||||
/**
|
||||
* 根据歌手统计被收藏的次数
|
||||
*
|
||||
* @param singer 歌手
|
||||
* @return 被收藏的次数
|
||||
*/
|
||||
long countBySinger(Singer singer);
|
||||
|
||||
/**
|
||||
* 根据用户和歌手删除收藏记录
|
||||
*
|
||||
* @param user 用户
|
||||
* @param singer 歌手
|
||||
*/
|
||||
void deleteByUserAndSinger(User user, Singer singer);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.test.musichouduan.repository;
|
||||
|
||||
import com.test.musichouduan.entity.Music;
|
||||
import com.test.musichouduan.entity.User;
|
||||
import com.test.musichouduan.entity.UserMusic;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 用户音乐关联数据访问层
|
||||
*/
|
||||
@Repository
|
||||
public interface UserMusicRepository extends JpaRepository<UserMusic, Long> {
|
||||
|
||||
/**
|
||||
* 判断用户是否已收藏音乐
|
||||
*
|
||||
* @param user 用户
|
||||
* @param music 音乐
|
||||
* @return 是否已收藏
|
||||
*/
|
||||
boolean existsByUserAndMusic(User user, Music music);
|
||||
|
||||
/**
|
||||
* 判断用户是否已收藏音乐
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param musicId 音乐ID
|
||||
* @return 是否已收藏
|
||||
*/
|
||||
boolean existsByUserIdAndMusicId(Long userId, Long musicId);
|
||||
|
||||
/**
|
||||
* 收藏音乐
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param musicId 音乐ID
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query(value = "INSERT INTO user_music (user_id, music_id, collect_time) VALUES (?1, ?2, NOW())", nativeQuery = true)
|
||||
void collectMusic(Long userId, Long musicId);
|
||||
|
||||
/**
|
||||
* 取消收藏音乐
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param musicId 音乐ID
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query(value = "DELETE FROM user_music WHERE user_id = ?1 AND music_id = ?2", nativeQuery = true)
|
||||
void uncollectMusic(Long userId, Long musicId);
|
||||
|
||||
/**
|
||||
* 获取用户收藏的音乐
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param pageable 分页
|
||||
* @return 用户音乐关联分页
|
||||
*/
|
||||
@Query("SELECT um FROM UserMusic um WHERE um.user.id = ?1")
|
||||
Page<UserMusic> findCollectedMusic(Long userId, Pageable pageable);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.test.musichouduan.repository;
|
||||
|
||||
import com.test.musichouduan.entity.Playlist;
|
||||
import com.test.musichouduan.entity.User;
|
||||
import com.test.musichouduan.entity.UserPlaylist;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 用户歌单关联数据访问层
|
||||
*/
|
||||
@Repository
|
||||
public interface UserPlaylistRepository extends JpaRepository<UserPlaylist, Long> {
|
||||
|
||||
/**
|
||||
* 判断用户是否已收藏歌单
|
||||
*
|
||||
* @param user 用户
|
||||
* @param playlist 歌单
|
||||
* @return 是否已收藏
|
||||
*/
|
||||
boolean existsByUserAndPlaylist(User user, Playlist playlist);
|
||||
|
||||
/**
|
||||
* 判断用户是否已收藏歌单
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param playlistId 歌单ID
|
||||
* @return 是否已收藏
|
||||
*/
|
||||
boolean existsByUserIdAndPlaylistId(Long userId, Long playlistId);
|
||||
|
||||
/**
|
||||
* 收藏歌单
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param playlistId 歌单ID
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query(value = "INSERT INTO user_playlist (user_id, playlist_id, collect_time) VALUES (?1, ?2, NOW())", nativeQuery = true)
|
||||
void collectPlaylist(Long userId, Long playlistId);
|
||||
|
||||
/**
|
||||
* 取消收藏歌单
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param playlistId 歌单ID
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query(value = "DELETE FROM user_playlist WHERE user_id = ?1 AND playlist_id = ?2", nativeQuery = true)
|
||||
void uncollectPlaylist(Long userId, Long playlistId);
|
||||
|
||||
/**
|
||||
* 获取用户收藏的歌单
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param pageable 分页
|
||||
* @return 歌单分页
|
||||
*/
|
||||
@Query("SELECT up.playlist FROM UserPlaylist up WHERE up.user.id = ?1")
|
||||
Page<Playlist> findCollectedPlaylist(Long userId, Pageable pageable);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.test.musichouduan.repository;
|
||||
|
||||
import com.test.musichouduan.entity.User;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 用户数据访问层
|
||||
*/
|
||||
@Repository
|
||||
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
|
||||
|
||||
/**
|
||||
* 根据用户名查找用户
|
||||
*
|
||||
* @param username 用户名
|
||||
* @return 用户
|
||||
*/
|
||||
Optional<User> findByUsername(String username);
|
||||
|
||||
/**
|
||||
* 根据用户名或邮箱查找用户
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param email 邮箱
|
||||
* @return 用户
|
||||
*/
|
||||
Optional<User> findByUsernameOrEmail(String username, String email);
|
||||
|
||||
/**
|
||||
* 判断用户名是否存在
|
||||
*
|
||||
* @param username 用户名
|
||||
* @return 是否存在
|
||||
*/
|
||||
boolean existsByUsername(String username);
|
||||
|
||||
/**
|
||||
* 判断邮箱是否存在
|
||||
*
|
||||
* @param email 邮箱
|
||||
* @return 是否存在
|
||||
*/
|
||||
boolean existsByEmail(String email);
|
||||
|
||||
/**
|
||||
* 统计指定时间范围内创建的用户数量
|
||||
*
|
||||
* @param start 开始时间
|
||||
* @param end 结束时间
|
||||
* @return 用户数量
|
||||
*/
|
||||
long countByCreateTimeBetween(LocalDateTime start, LocalDateTime end);
|
||||
|
||||
/**
|
||||
* 查询最新注册的用户
|
||||
*
|
||||
* @param pageable 分页
|
||||
* @return 用户列表
|
||||
*/
|
||||
@Query("SELECT u FROM User u ORDER BY u.createTime DESC")
|
||||
List<User> findLatestUsers(Pageable pageable);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.test.musichouduan.repository;
|
||||
|
||||
import com.test.musichouduan.entity.Singer;
|
||||
import com.test.musichouduan.entity.User;
|
||||
import com.test.musichouduan.entity.UserSinger;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 用户歌手关联数据访问层
|
||||
*/
|
||||
@Repository
|
||||
public interface UserSingerRepository extends JpaRepository<UserSinger, Long> {
|
||||
|
||||
/**
|
||||
* 判断用户是否已收藏歌手
|
||||
*
|
||||
* @param user 用户
|
||||
* @param singer 歌手
|
||||
* @return 是否已收藏
|
||||
*/
|
||||
boolean existsByUserAndSinger(User user, Singer singer);
|
||||
|
||||
/**
|
||||
* 判断用户是否已收藏歌手
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param singerId 歌手ID
|
||||
* @return 是否已收藏
|
||||
*/
|
||||
boolean existsByUserIdAndSingerId(Long userId, Long singerId);
|
||||
|
||||
/**
|
||||
* 收藏歌手
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param singerId 歌手ID
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query(value = "INSERT INTO user_singer (user_id, singer_id, collect_time) VALUES (?1, ?2, NOW())", nativeQuery = true)
|
||||
void collectSinger(Long userId, Long singerId);
|
||||
|
||||
/**
|
||||
* 取消收藏歌手
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param singerId 歌手ID
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query(value = "DELETE FROM user_singer WHERE user_id = ?1 AND singer_id = ?2", nativeQuery = true)
|
||||
void uncollectSinger(Long userId, Long singerId);
|
||||
|
||||
/**
|
||||
* 获取用户收藏的歌手
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param pageable 分页
|
||||
* @return 歌手分页
|
||||
*/
|
||||
@Query("SELECT us.singer FROM UserSinger us WHERE us.user.id = ?1")
|
||||
Page<Singer> findCollectedSinger(Long userId, Pageable pageable);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.test.musichouduan.service;
|
||||
|
||||
import com.test.musichouduan.dto.BannerDTO;
|
||||
import com.test.musichouduan.dto.PageResult;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 轮播图服务接口
|
||||
*/
|
||||
public interface BannerService {
|
||||
|
||||
/**
|
||||
* 根据ID获取轮播图
|
||||
*
|
||||
* @param id 轮播图ID
|
||||
* @return 轮播图DTO
|
||||
*/
|
||||
BannerDTO getBannerById(Long id);
|
||||
|
||||
/**
|
||||
* 获取轮播图列表
|
||||
*
|
||||
* @return 轮播图列表
|
||||
*/
|
||||
List<BannerDTO> getBannerList();
|
||||
|
||||
/**
|
||||
* 分页获取轮播图列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResult<BannerDTO> getBannerPage(Integer page, Integer size);
|
||||
|
||||
/**
|
||||
* 添加轮播图
|
||||
*
|
||||
* @param bannerDTO 轮播图DTO
|
||||
* @param image 图片文件
|
||||
* @return 轮播图DTO
|
||||
*/
|
||||
BannerDTO addBanner(BannerDTO bannerDTO, MultipartFile image);
|
||||
|
||||
/**
|
||||
* 更新轮播图
|
||||
*
|
||||
* @param id 轮播图ID
|
||||
* @param bannerDTO 轮播图DTO
|
||||
* @param image 图片文件
|
||||
* @return 轮播图DTO
|
||||
*/
|
||||
BannerDTO updateBanner(Long id, BannerDTO bannerDTO, MultipartFile image);
|
||||
|
||||
/**
|
||||
* 删除轮播图
|
||||
*
|
||||
* @param id 轮播图ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean deleteBanner(Long id);
|
||||
|
||||
/**
|
||||
* 更新轮播图状态
|
||||
*
|
||||
* @param id 轮播图ID
|
||||
* @param status 状态
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean updateBannerStatus(Long id, Integer status);
|
||||
|
||||
/**
|
||||
* 更新轮播图排序
|
||||
*
|
||||
* @param id 轮播图ID
|
||||
* @param orderNum 排序号
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean updateBannerOrder(Long id, Integer orderNum);
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.test.musichouduan.service;
|
||||
|
||||
import com.test.musichouduan.dto.CommentDTO;
|
||||
import com.test.musichouduan.dto.PageResult;
|
||||
|
||||
/**
|
||||
* 评论服务接口
|
||||
*/
|
||||
public interface CommentService {
|
||||
|
||||
/**
|
||||
* 根据ID获取评论
|
||||
*
|
||||
* @param id 评论ID
|
||||
* @return 评论DTO
|
||||
*/
|
||||
CommentDTO getCommentById(Long id);
|
||||
|
||||
/**
|
||||
* 分页获取评论列表
|
||||
*
|
||||
* @param targetId 目标ID
|
||||
* @param targetType 目标类型
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResult<CommentDTO> getCommentList(Long targetId, Integer targetType, Integer page, Integer size);
|
||||
|
||||
/**
|
||||
* 添加评论
|
||||
*
|
||||
* @param commentDTO 评论DTO
|
||||
* @return 评论DTO
|
||||
*/
|
||||
CommentDTO addComment(CommentDTO commentDTO);
|
||||
|
||||
/**
|
||||
* 删除评论
|
||||
*
|
||||
* @param id 评论ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean deleteComment(Long id);
|
||||
|
||||
/**
|
||||
* 获取用户的评论列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResult<CommentDTO> getUserCommentList(Long userId, Integer page, Integer size);
|
||||
|
||||
/**
|
||||
* 管理员获取所有评论列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @param keyword 关键字
|
||||
* @param targetType 目标类型
|
||||
* @param status 状态
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResult<CommentDTO> getAdminCommentList(Integer page, Integer size, String keyword, Integer targetType, Integer status);
|
||||
|
||||
/**
|
||||
* 更新评论状态
|
||||
*
|
||||
* @param id 评论ID
|
||||
* @param status 状态
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean updateCommentStatus(Long id, Integer status);
|
||||
|
||||
/**
|
||||
* 管理员删除评论
|
||||
*
|
||||
* @param id 评论ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean adminDeleteComment(Long id);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.test.musichouduan.service;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 文件服务接口
|
||||
*/
|
||||
public interface FileService {
|
||||
|
||||
/**
|
||||
* 上传图片
|
||||
*
|
||||
* @param file 图片文件
|
||||
* @return 图片URL
|
||||
*/
|
||||
String uploadImage(MultipartFile file);
|
||||
|
||||
/**
|
||||
* 上传音乐
|
||||
*
|
||||
* @param file 音乐文件
|
||||
* @return 音乐URL
|
||||
*/
|
||||
String uploadMusic(MultipartFile file);
|
||||
|
||||
/**
|
||||
* 上传歌词
|
||||
*
|
||||
* @param file 歌词文件
|
||||
* @return 歌词内容
|
||||
*/
|
||||
String uploadLyric(MultipartFile file);
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*
|
||||
* @param url 文件URL
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean deleteFile(String url);
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package com.test.musichouduan.service;
|
||||
|
||||
import com.test.musichouduan.dto.MusicDTO;
|
||||
import com.test.musichouduan.dto.PageResult;
|
||||
import com.test.musichouduan.entity.Music;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 音乐服务接口
|
||||
*/
|
||||
public interface MusicService {
|
||||
|
||||
/**
|
||||
* 根据ID获取音乐
|
||||
*
|
||||
* @param id 音乐ID
|
||||
* @return 音乐DTO
|
||||
*/
|
||||
MusicDTO getMusicById(Long id);
|
||||
|
||||
/**
|
||||
* 分页获取音乐列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @param keyword 关键字
|
||||
* @param typeId 类型ID
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResult<MusicDTO> getMusicList(Integer page, Integer size, String keyword, Long typeId);
|
||||
|
||||
/**
|
||||
* 根据歌手ID获取音乐列表
|
||||
*
|
||||
* @param singerId 歌手ID
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResult<MusicDTO> getMusicListBySinger(Long singerId, Integer page, Integer size);
|
||||
|
||||
/**
|
||||
* 获取最新音乐列表
|
||||
*
|
||||
* @param limit 限制数量
|
||||
* @return 音乐列表
|
||||
*/
|
||||
List<MusicDTO> getLatestMusic(Integer limit);
|
||||
|
||||
/**
|
||||
* 获取热门音乐列表
|
||||
*
|
||||
* @param limit 限制数量
|
||||
* @return 音乐列表
|
||||
*/
|
||||
List<MusicDTO> getHotMusic(Integer limit);
|
||||
|
||||
/**
|
||||
* 添加音乐
|
||||
*
|
||||
* @param musicDTO 音乐DTO
|
||||
* @param file 音乐文件
|
||||
* @param cover 封面文件
|
||||
* @return 音乐DTO
|
||||
*/
|
||||
MusicDTO addMusic(MusicDTO musicDTO, MultipartFile file, MultipartFile cover);
|
||||
|
||||
/**
|
||||
* 更新音乐
|
||||
*
|
||||
* @param id 音乐ID
|
||||
* @param musicDTO 音乐DTO
|
||||
* @param file 音乐文件
|
||||
* @param cover 封面文件
|
||||
* @param lyric 歌词文件
|
||||
* @return 音乐DTO
|
||||
*/
|
||||
MusicDTO updateMusic(Long id, MusicDTO musicDTO, MultipartFile file, MultipartFile cover, MultipartFile lyric);
|
||||
|
||||
/**
|
||||
* 删除音乐
|
||||
*
|
||||
* @param id 音乐ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean deleteMusic(Long id);
|
||||
|
||||
/**
|
||||
* 收藏/取消收藏音乐
|
||||
*
|
||||
* @param id 音乐ID
|
||||
* @param userId 用户ID
|
||||
* @return 是否收藏成功
|
||||
*/
|
||||
boolean toggleCollectMusic(Long id, Long userId);
|
||||
|
||||
/**
|
||||
* 获取用户收藏的音乐列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResult<MusicDTO> getCollectedMusic(Long userId, Integer page, Integer size);
|
||||
|
||||
/**
|
||||
* 播放音乐
|
||||
*
|
||||
* @param id 音乐ID
|
||||
* @return 音乐DTO
|
||||
*/
|
||||
MusicDTO playMusic(Long id);
|
||||
|
||||
/**
|
||||
* 根据ID获取音乐实体
|
||||
*
|
||||
* @param id 音乐ID
|
||||
* @return 音乐实体
|
||||
*/
|
||||
Music getById(Long id);
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package com.test.musichouduan.service;
|
||||
|
||||
import com.test.musichouduan.dto.PageResult;
|
||||
import com.test.musichouduan.dto.PlaylistDTO;
|
||||
import com.test.musichouduan.entity.Playlist;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 歌单服务接口
|
||||
*/
|
||||
public interface PlaylistService {
|
||||
|
||||
/**
|
||||
* 根据ID获取歌单
|
||||
*
|
||||
* @param id 歌单ID
|
||||
* @return 歌单DTO
|
||||
*/
|
||||
PlaylistDTO getPlaylistById(Long id);
|
||||
|
||||
/**
|
||||
* 分页获取歌单列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @param keyword 关键字
|
||||
* @param typeId 类型ID
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResult<PlaylistDTO> getPlaylistList(Integer page, Integer size, String keyword, Long typeId);
|
||||
|
||||
/**
|
||||
* 获取最新歌单列表
|
||||
*
|
||||
* @param limit 限制数量
|
||||
* @return 歌单列表
|
||||
*/
|
||||
List<PlaylistDTO> getLatestPlaylist(Integer limit);
|
||||
|
||||
/**
|
||||
* 获取热门歌单列表
|
||||
*
|
||||
* @param limit 限制数量
|
||||
* @return 歌单列表
|
||||
*/
|
||||
List<PlaylistDTO> getHotPlaylist(Integer limit);
|
||||
|
||||
/**
|
||||
* 获取官方歌单列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResult<PlaylistDTO> getOfficialPlaylist(Integer page, Integer size);
|
||||
|
||||
/**
|
||||
* 添加歌单
|
||||
*
|
||||
* @param playlistDTO 歌单DTO
|
||||
* @param cover 封面文件
|
||||
* @return 歌单DTO
|
||||
*/
|
||||
PlaylistDTO addPlaylist(PlaylistDTO playlistDTO, MultipartFile cover);
|
||||
|
||||
/**
|
||||
* 更新歌单
|
||||
*
|
||||
* @param id 歌单ID
|
||||
* @param playlistDTO 歌单DTO
|
||||
* @param cover 封面文件
|
||||
* @return 歌单DTO
|
||||
*/
|
||||
PlaylistDTO updatePlaylist(Long id, PlaylistDTO playlistDTO, MultipartFile cover);
|
||||
|
||||
/**
|
||||
* 删除歌单
|
||||
*
|
||||
* @param id 歌单ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean deletePlaylist(Long id);
|
||||
|
||||
/**
|
||||
* 收藏歌单
|
||||
*
|
||||
* @param id 歌单ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean collectPlaylist(Long id);
|
||||
|
||||
/**
|
||||
* 取消收藏歌单
|
||||
*
|
||||
* @param id 歌单ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean uncollectPlaylist(Long id);
|
||||
|
||||
/**
|
||||
* 获取用户收藏的歌单列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResult<PlaylistDTO> getCollectedPlaylist(Integer page, Integer size);
|
||||
|
||||
/**
|
||||
* 获取用户创建的歌单列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResult<PlaylistDTO> getUserPlaylist(Integer page, Integer size);
|
||||
|
||||
/**
|
||||
* 向歌单添加音乐
|
||||
*
|
||||
* @param id 歌单ID
|
||||
* @param musicId 音乐ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean addMusicToPlaylist(Long id, Long musicId);
|
||||
|
||||
/**
|
||||
* 从歌单移除音乐
|
||||
*
|
||||
* @param id 歌单ID
|
||||
* @param musicId 音乐ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean removeMusicFromPlaylist(Long id, Long musicId);
|
||||
|
||||
/**
|
||||
* 播放歌单
|
||||
*
|
||||
* @param id 歌单ID
|
||||
* @return 歌单DTO
|
||||
*/
|
||||
PlaylistDTO playPlaylist(Long id);
|
||||
|
||||
/**
|
||||
* 根据ID获取歌单实体
|
||||
*
|
||||
* @param id 歌单ID
|
||||
* @return 歌单实体
|
||||
*/
|
||||
Playlist getById(Long id);
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.test.musichouduan.service;
|
||||
|
||||
import com.test.musichouduan.dto.PageResult;
|
||||
import com.test.musichouduan.dto.SingerDTO;
|
||||
import com.test.musichouduan.entity.Singer;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 歌手服务接口
|
||||
*/
|
||||
public interface SingerService {
|
||||
|
||||
/**
|
||||
* 根据ID获取歌手
|
||||
*
|
||||
* @param id 歌手ID
|
||||
* @return 歌手DTO
|
||||
*/
|
||||
SingerDTO getSingerById(Long id);
|
||||
|
||||
/**
|
||||
* 分页获取歌手列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @param keyword 关键字
|
||||
* @param typeId 类型ID
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResult<SingerDTO> getSingerList(Integer page, Integer size, String keyword, Long typeId);
|
||||
|
||||
/**
|
||||
* 获取最新歌手列表
|
||||
*
|
||||
* @param limit 限制数量
|
||||
* @return 歌手列表
|
||||
*/
|
||||
List<SingerDTO> getLatestSinger(Integer limit);
|
||||
|
||||
/**
|
||||
* 获取热门歌手列表
|
||||
*
|
||||
* @param limit 限制数量
|
||||
* @return 歌手列表
|
||||
*/
|
||||
List<SingerDTO> getHotSinger(Integer limit);
|
||||
|
||||
/**
|
||||
* 添加歌手
|
||||
*
|
||||
* @param singerDTO 歌手DTO
|
||||
* @param avatar 头像文件
|
||||
* @return 歌手DTO
|
||||
*/
|
||||
SingerDTO addSinger(SingerDTO singerDTO, MultipartFile avatar);
|
||||
|
||||
/**
|
||||
* 更新歌手
|
||||
*
|
||||
* @param id 歌手ID
|
||||
* @param singerDTO 歌手DTO
|
||||
* @param avatar 头像文件
|
||||
* @return 歌手DTO
|
||||
*/
|
||||
SingerDTO updateSinger(Long id, SingerDTO singerDTO, MultipartFile avatar);
|
||||
|
||||
/**
|
||||
* 删除歌手
|
||||
*
|
||||
* @param id 歌手ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean deleteSinger(Long id);
|
||||
|
||||
/**
|
||||
* 收藏歌手
|
||||
*
|
||||
* @param id 歌手ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean collectSinger(Long id);
|
||||
|
||||
/**
|
||||
* 取消收藏歌手
|
||||
*
|
||||
* @param id 歌手ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean uncollectSinger(Long id);
|
||||
|
||||
/**
|
||||
* 获取用户收藏的歌手列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResult<SingerDTO> getCollectedSinger(Integer page, Integer size);
|
||||
|
||||
/**
|
||||
* 添加音乐到歌手
|
||||
* @param singerId 歌手ID
|
||||
* @param musicId 音乐ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean addMusicToSinger(Long singerId, Long musicId);
|
||||
|
||||
/**
|
||||
* 从歌手移除音乐
|
||||
* @param singerId 歌手ID
|
||||
* @param musicId 音乐ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean removeMusicFromSinger(Long singerId, Long musicId);
|
||||
|
||||
/**
|
||||
* 根据ID获取歌手实体
|
||||
*
|
||||
* @param id 歌手ID
|
||||
* @return 歌手实体
|
||||
*/
|
||||
Singer getById(Long id);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.test.musichouduan.service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 统计服务接口
|
||||
*/
|
||||
public interface StatService {
|
||||
|
||||
/**
|
||||
* 获取总体统计数据
|
||||
*
|
||||
* @return 统计数据
|
||||
*/
|
||||
Map<String, Object> getTotalStats();
|
||||
|
||||
/**
|
||||
* 获取用户增长趋势
|
||||
*
|
||||
* @param days 天数
|
||||
* @return 统计数据
|
||||
*/
|
||||
Map<String, Object> getUserGrowthTrend(Integer days);
|
||||
|
||||
/**
|
||||
* 获取音乐类型分布
|
||||
*
|
||||
* @return 统计数据
|
||||
*/
|
||||
Map<String, Object> getMusicTypeDistribution();
|
||||
|
||||
/**
|
||||
* 获取播放量统计
|
||||
*
|
||||
* @param days 天数
|
||||
* @return 统计数据
|
||||
*/
|
||||
Map<String, Object> getPlayCountStats(Integer days);
|
||||
|
||||
/**
|
||||
* 获取最新用户列表
|
||||
*
|
||||
* @param limit 限制数量
|
||||
* @return 统计数据
|
||||
*/
|
||||
Map<String, Object> getLatestUsers(Integer limit);
|
||||
|
||||
/**
|
||||
* 获取最新音乐列表
|
||||
*
|
||||
* @param limit 限制数量
|
||||
* @return 统计数据
|
||||
*/
|
||||
Map<String, Object> getLatestMusics(Integer limit);
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package com.test.musichouduan.service;
|
||||
|
||||
import com.test.musichouduan.dto.MusicTypeDTO;
|
||||
import com.test.musichouduan.dto.PageResult;
|
||||
import com.test.musichouduan.dto.PlaylistTypeDTO;
|
||||
import com.test.musichouduan.dto.SingerTypeDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 类型服务接口
|
||||
*/
|
||||
public interface TypeService {
|
||||
|
||||
/**
|
||||
* 获取音乐类型列表
|
||||
*
|
||||
* @return 音乐类型列表
|
||||
*/
|
||||
List<MusicTypeDTO> getMusicTypeList();
|
||||
|
||||
/**
|
||||
* 分页获取音乐类型列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @param keyword 关键字
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResult<MusicTypeDTO> getMusicTypePage(Integer page, Integer size, String keyword);
|
||||
|
||||
/**
|
||||
* 添加音乐类型
|
||||
*
|
||||
* @param musicTypeDTO 音乐类型DTO
|
||||
* @return 音乐类型DTO
|
||||
*/
|
||||
MusicTypeDTO addMusicType(MusicTypeDTO musicTypeDTO);
|
||||
|
||||
/**
|
||||
* 更新音乐类型
|
||||
*
|
||||
* @param id 音乐类型ID
|
||||
* @param musicTypeDTO 音乐类型DTO
|
||||
* @return 音乐类型DTO
|
||||
*/
|
||||
MusicTypeDTO updateMusicType(Long id, MusicTypeDTO musicTypeDTO);
|
||||
|
||||
/**
|
||||
* 删除音乐类型
|
||||
*
|
||||
* @param id 音乐类型ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean deleteMusicType(Long id);
|
||||
|
||||
/**
|
||||
* 获取歌手类型列表
|
||||
*
|
||||
* @return 歌手类型列表
|
||||
*/
|
||||
List<SingerTypeDTO> getSingerTypeList();
|
||||
|
||||
/**
|
||||
* 分页获取歌手类型列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @param keyword 关键字
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResult<SingerTypeDTO> getSingerTypePage(Integer page, Integer size, String keyword);
|
||||
|
||||
/**
|
||||
* 添加歌手类型
|
||||
*
|
||||
* @param singerTypeDTO 歌手类型DTO
|
||||
* @return 歌手类型DTO
|
||||
*/
|
||||
SingerTypeDTO addSingerType(SingerTypeDTO singerTypeDTO);
|
||||
|
||||
/**
|
||||
* 更新歌手类型
|
||||
*
|
||||
* @param id 歌手类型ID
|
||||
* @param singerTypeDTO 歌手类型DTO
|
||||
* @return 歌手类型DTO
|
||||
*/
|
||||
SingerTypeDTO updateSingerType(Long id, SingerTypeDTO singerTypeDTO);
|
||||
|
||||
/**
|
||||
* 删除歌手类型
|
||||
*
|
||||
* @param id 歌手类型ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean deleteSingerType(Long id);
|
||||
|
||||
/**
|
||||
* 获取歌单类型列表
|
||||
*
|
||||
* @return 歌单类型列表
|
||||
*/
|
||||
List<PlaylistTypeDTO> getPlaylistTypeList();
|
||||
|
||||
/**
|
||||
* 分页获取歌单类型列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @param keyword 关键字
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResult<PlaylistTypeDTO> getPlaylistTypePage(Integer page, Integer size, String keyword);
|
||||
|
||||
/**
|
||||
* 添加歌单类型
|
||||
*
|
||||
* @param playlistTypeDTO 歌单类型DTO
|
||||
* @return 歌单类型DTO
|
||||
*/
|
||||
PlaylistTypeDTO addPlaylistType(PlaylistTypeDTO playlistTypeDTO);
|
||||
|
||||
/**
|
||||
* 更新歌单类型
|
||||
*
|
||||
* @param id 歌单类型ID
|
||||
* @param playlistTypeDTO 歌单类型DTO
|
||||
* @return 歌单类型DTO
|
||||
*/
|
||||
PlaylistTypeDTO updatePlaylistType(Long id, PlaylistTypeDTO playlistTypeDTO);
|
||||
|
||||
/**
|
||||
* 删除歌单类型
|
||||
*
|
||||
* @param id 歌单类型ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean deletePlaylistType(Long id);
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.test.musichouduan.service;
|
||||
|
||||
import com.test.musichouduan.dto.*;
|
||||
import com.test.musichouduan.entity.User;
|
||||
|
||||
/**
|
||||
* 用户服务接口
|
||||
*/
|
||||
public interface UserService {
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
*
|
||||
* @param request 注册请求
|
||||
* @return 用户DTO
|
||||
*/
|
||||
UserDTO register(RegisterRequest request);
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
*
|
||||
* @param request 登录请求
|
||||
* @return 登录结果DTO,包含用户信息和令牌信息
|
||||
*/
|
||||
LoginResultDTO login(LoginRequest request);
|
||||
|
||||
/**
|
||||
* 获取当前登录用户
|
||||
*
|
||||
* @return 用户DTO
|
||||
*/
|
||||
UserDTO getCurrentUser();
|
||||
|
||||
/**
|
||||
* 根据ID获取用户
|
||||
*
|
||||
* @param id 用户ID
|
||||
* @return 用户DTO
|
||||
*/
|
||||
UserDTO getUserById(Long id);
|
||||
|
||||
/**
|
||||
* 根据用户名获取用户
|
||||
*
|
||||
* @param username 用户名
|
||||
* @return 用户DTO
|
||||
*/
|
||||
UserDTO getUserByUsername(String username);
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
*
|
||||
* @param request 用户信息更新请求
|
||||
* @return 用户DTO
|
||||
*/
|
||||
UserDTO updateUser(UserUpdateRequest request);
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*
|
||||
* @param request 修改密码请求
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean updatePassword(PasswordUpdateRequest request);
|
||||
|
||||
/**
|
||||
* 获取用户统计数据
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 用户DTO
|
||||
*/
|
||||
UserDTO getUserStats(Long userId);
|
||||
|
||||
/**
|
||||
* 分页获取用户列表
|
||||
*
|
||||
* @param page 页码
|
||||
* @param size 每页大小
|
||||
* @param keyword 关键字
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResult<UserDTO> getUserList(Integer page, Integer size, String keyword);
|
||||
|
||||
/**
|
||||
* 根据ID删除用户
|
||||
*
|
||||
* @param id 用户ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean deleteUser(Long id);
|
||||
|
||||
/**
|
||||
* 更新用户状态
|
||||
*
|
||||
* @param id 用户ID
|
||||
* @param status 状态
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean updateUserStatus(Long id, Integer status);
|
||||
|
||||
/**
|
||||
* 管理员更新用户信息
|
||||
*
|
||||
* @param id 用户ID
|
||||
* @param request 用户信息更新请求
|
||||
* @return 用户DTO
|
||||
*/
|
||||
UserDTO adminUpdateUser(Long id, UserUpdateRequest request);
|
||||
|
||||
/**
|
||||
* 根据ID获取用户实体
|
||||
*
|
||||
* @param id 用户ID
|
||||
* @return 用户实体
|
||||
*/
|
||||
User getById(Long id);
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package com.test.musichouduan.service.impl;
|
||||
|
||||
import com.test.musichouduan.dto.BannerDTO;
|
||||
import com.test.musichouduan.dto.PageResult;
|
||||
import com.test.musichouduan.entity.Banner;
|
||||
import com.test.musichouduan.exception.BusinessException;
|
||||
import com.test.musichouduan.repository.BannerRepository;
|
||||
import com.test.musichouduan.service.BannerService;
|
||||
import com.test.musichouduan.service.FileService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 轮播图服务实现类
|
||||
*/
|
||||
@Service
|
||||
public class BannerServiceImpl implements BannerService {
|
||||
|
||||
@Autowired
|
||||
private BannerRepository bannerRepository;
|
||||
|
||||
@Autowired
|
||||
private FileService fileService;
|
||||
|
||||
@Override
|
||||
public BannerDTO getBannerById(Long id) {
|
||||
Banner banner = bannerRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("轮播图不存在"));
|
||||
return convertToDTO(banner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BannerDTO> getBannerList() {
|
||||
// 只获取启用状态的轮播图,并按排序号排序
|
||||
List<Banner> banners = bannerRepository.findByStatusOrderByOrderNumAsc(1);
|
||||
return banners.stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<BannerDTO> getBannerPage(Integer page, Integer size) {
|
||||
Page<Banner> bannerPage = bannerRepository.findAll(
|
||||
PageRequest.of(page - 1, size, Sort.by(Sort.Direction.ASC, "orderNum"))
|
||||
);
|
||||
|
||||
List<BannerDTO> bannerDTOs = bannerPage.getContent().stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new PageResult<>(
|
||||
bannerDTOs,
|
||||
bannerPage.getTotalElements(),
|
||||
page,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public BannerDTO addBanner(BannerDTO bannerDTO, MultipartFile image) {
|
||||
// 上传图片
|
||||
String imageUrl = fileService.uploadImage(image);
|
||||
|
||||
// 创建轮播图实体
|
||||
Banner banner = new Banner();
|
||||
BeanUtils.copyProperties(bannerDTO, banner);
|
||||
banner.setImageUrl(imageUrl);
|
||||
banner.setStatus(banner.getStatus() != null ? banner.getStatus() : 1);
|
||||
|
||||
// 保存轮播图
|
||||
Banner savedBanner = bannerRepository.save(banner);
|
||||
|
||||
return convertToDTO(savedBanner);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public BannerDTO updateBanner(Long id, BannerDTO bannerDTO, MultipartFile image) {
|
||||
// 获取轮播图
|
||||
Banner banner = bannerRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("轮播图不存在"));
|
||||
|
||||
// 如果有新图片,则上传并更新URL
|
||||
if (image != null && !image.isEmpty()) {
|
||||
// 删除旧图片
|
||||
if (banner.getImageUrl() != null) {
|
||||
fileService.deleteFile(banner.getImageUrl());
|
||||
}
|
||||
|
||||
// 上传新图片
|
||||
String imageUrl = fileService.uploadImage(image);
|
||||
banner.setImageUrl(imageUrl);
|
||||
}
|
||||
|
||||
// 更新轮播图信息
|
||||
banner.setTitle(bannerDTO.getTitle());
|
||||
banner.setTargetType(bannerDTO.getTargetType());
|
||||
banner.setTargetId(bannerDTO.getTargetId());
|
||||
banner.setLinkUrl(bannerDTO.getLinkUrl());
|
||||
if (bannerDTO.getOrderNum() != null) {
|
||||
banner.setOrderNum(bannerDTO.getOrderNum());
|
||||
}
|
||||
if (bannerDTO.getStatus() != null) {
|
||||
banner.setStatus(bannerDTO.getStatus());
|
||||
}
|
||||
|
||||
// 保存轮播图
|
||||
Banner updatedBanner = bannerRepository.save(banner);
|
||||
|
||||
return convertToDTO(updatedBanner);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean deleteBanner(Long id) {
|
||||
// 获取轮播图
|
||||
Banner banner = bannerRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("轮播图不存在"));
|
||||
|
||||
// 删除图片
|
||||
if (banner.getImageUrl() != null) {
|
||||
fileService.deleteFile(banner.getImageUrl());
|
||||
}
|
||||
|
||||
// 删除轮播图
|
||||
bannerRepository.delete(banner);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean updateBannerStatus(Long id, Integer status) {
|
||||
// 获取轮播图
|
||||
Banner banner = bannerRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("轮播图不存在"));
|
||||
|
||||
// 更新状态
|
||||
banner.setStatus(status);
|
||||
|
||||
// 保存轮播图
|
||||
bannerRepository.save(banner);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean updateBannerOrder(Long id, Integer orderNum) {
|
||||
// 获取轮播图
|
||||
Banner banner = bannerRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("轮播图不存在"));
|
||||
|
||||
// 更新排序号
|
||||
banner.setOrderNum(orderNum);
|
||||
|
||||
// 保存轮播图
|
||||
bannerRepository.save(banner);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将实体转换为DTO
|
||||
*
|
||||
* @param banner 轮播图实体
|
||||
* @return 轮播图DTO
|
||||
*/
|
||||
private BannerDTO convertToDTO(Banner banner) {
|
||||
BannerDTO bannerDTO = new BannerDTO();
|
||||
BeanUtils.copyProperties(banner, bannerDTO);
|
||||
// 处理字段名不一致的情况
|
||||
// 不需要手动设置,因为BeanUtils会自动匹配相同名称的属性
|
||||
return bannerDTO;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
package com.test.musichouduan.service.impl;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.test.musichouduan.dto.CommentDTO;
|
||||
import com.test.musichouduan.dto.PageResult;
|
||||
import com.test.musichouduan.dto.UserDTO;
|
||||
import com.test.musichouduan.entity.Comment;
|
||||
import com.test.musichouduan.entity.User;
|
||||
import com.test.musichouduan.exception.BusinessException;
|
||||
import com.test.musichouduan.repository.CommentRepository;
|
||||
import com.test.musichouduan.repository.UserRepository;
|
||||
import com.test.musichouduan.service.CommentService;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate; // 添加导入
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 评论服务实现类
|
||||
*/
|
||||
@Service
|
||||
public class CommentServiceImpl implements CommentService {
|
||||
|
||||
@Autowired
|
||||
private CommentRepository commentRepository;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
private SimpMessagingTemplate messagingTemplate; // 注入 SimpMessagingTemplate
|
||||
|
||||
@Override
|
||||
public CommentDTO getCommentById(Long id) {
|
||||
Comment comment = commentRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("评论不存在"));
|
||||
return convertToDTO(comment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CommentDTO> getCommentList(Long targetId, Integer targetType, Integer page, Integer size) {
|
||||
// 获取父评论
|
||||
Page<Comment> commentPage = commentRepository.findByTargetIdAndTargetTypeAndParentIdIsNullOrderByCreateTimeDesc(
|
||||
targetId, targetType, PageRequest.of(page - 1, size)
|
||||
);
|
||||
|
||||
// 转换为DTO
|
||||
List<CommentDTO> commentDTOs = commentPage.getContent().stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 获取子评论
|
||||
for (CommentDTO commentDTO : commentDTOs) {
|
||||
List<Comment> children = commentRepository.findByParentIdOrderByCreateTimeAsc(commentDTO.getId());
|
||||
if (!children.isEmpty()) {
|
||||
commentDTO.setChildren(children.stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList()));
|
||||
} else {
|
||||
commentDTO.setChildren(new ArrayList<>());
|
||||
}
|
||||
}
|
||||
|
||||
return new PageResult<>(
|
||||
commentDTOs,
|
||||
commentPage.getTotalElements(),
|
||||
page,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommentDTO addComment(CommentDTO commentDTO) {
|
||||
// 获取当前登录用户
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new BusinessException("用户不存在"));
|
||||
|
||||
// 创建评论实体
|
||||
Comment comment = new Comment();
|
||||
comment.setContent(commentDTO.getContent());
|
||||
comment.setUser(user);
|
||||
comment.setTargetId(commentDTO.getTargetId());
|
||||
comment.setTargetType(commentDTO.getTargetType());
|
||||
comment.setParentId(commentDTO.getParentId());
|
||||
comment.setStatus(1); // 默认状态为正常
|
||||
|
||||
// 保存评论
|
||||
Comment savedComment = commentRepository.save(comment);
|
||||
|
||||
// 将新评论转换为DTO
|
||||
CommentDTO savedCommentDTO = convertToDTO(savedComment);
|
||||
|
||||
// 构建 WebSocket 主题路径
|
||||
String destination = String.format("/topic/comments/%d/%d", savedComment.getTargetType(), savedComment.getTargetId());
|
||||
|
||||
// 通过 WebSocket 发送新评论
|
||||
messagingTemplate.convertAndSend(destination, savedCommentDTO);
|
||||
|
||||
return savedCommentDTO; // 返回转换后的 DTO
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean deleteComment(Long id) {
|
||||
// 获取评论
|
||||
Comment comment = commentRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("评论不存在"));
|
||||
|
||||
// 获取当前登录用户
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
|
||||
// 检查是否是评论作者或管理员
|
||||
if (!comment.getUser().getId().equals(userId) && !StpUtil.hasRole("admin")) {
|
||||
throw new BusinessException("无权删除该评论");
|
||||
}
|
||||
|
||||
// 删除评论
|
||||
commentRepository.delete(comment);
|
||||
|
||||
// 如果是父评论,同时删除子评论
|
||||
if (comment.getParentId() == null) {
|
||||
List<Comment> children = commentRepository.findByParentIdOrderByCreateTimeAsc(id);
|
||||
if (!children.isEmpty()) {
|
||||
commentRepository.deleteAll(children);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CommentDTO> getUserCommentList(Long userId, Integer page, Integer size) {
|
||||
// 获取用户
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new BusinessException("用户不存在"));
|
||||
|
||||
// 获取用户评论
|
||||
Page<Comment> commentPage = commentRepository.findByUserOrderByCreateTimeDesc(
|
||||
user, PageRequest.of(page - 1, size)
|
||||
);
|
||||
|
||||
// 转换为DTO
|
||||
List<CommentDTO> commentDTOs = commentPage.getContent().stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new PageResult<>(
|
||||
commentDTOs,
|
||||
commentPage.getTotalElements(),
|
||||
page,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CommentDTO> getAdminCommentList(Integer page, Integer size, String keyword, Integer targetType, Integer status) {
|
||||
// 构建查询条件
|
||||
Specification<Comment> spec = (root, query, criteriaBuilder) -> {
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
|
||||
// 关键字搜索
|
||||
if (keyword != null && !keyword.isEmpty()) {
|
||||
predicates.add(criteriaBuilder.like(root.get("content"), "%" + keyword + "%"));
|
||||
}
|
||||
|
||||
// 目标类型筛选
|
||||
if (targetType != null) {
|
||||
predicates.add(criteriaBuilder.equal(root.get("targetType"), targetType));
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
if (status != null) {
|
||||
predicates.add(criteriaBuilder.equal(root.get("status"), status));
|
||||
}
|
||||
|
||||
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
|
||||
};
|
||||
|
||||
// 执行查询
|
||||
Page<Comment> commentPage = commentRepository.findAll(
|
||||
spec, PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createTime"))
|
||||
);
|
||||
|
||||
// 转换为DTO
|
||||
List<CommentDTO> commentDTOs = commentPage.getContent().stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new PageResult<>(
|
||||
commentDTOs,
|
||||
commentPage.getTotalElements(),
|
||||
page,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean updateCommentStatus(Long id, Integer status) {
|
||||
// 获取评论
|
||||
Comment comment = commentRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("评论不存在"));
|
||||
|
||||
// 更新状态
|
||||
comment.setStatus(status);
|
||||
commentRepository.save(comment);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean adminDeleteComment(Long id) {
|
||||
// 获取评论
|
||||
Comment comment = commentRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("评论不存在"));
|
||||
|
||||
// 删除评论
|
||||
commentRepository.delete(comment);
|
||||
|
||||
// 如果是父评论,同时删除子评论
|
||||
if (comment.getParentId() == null) {
|
||||
List<Comment> children = commentRepository.findByParentIdOrderByCreateTimeAsc(id);
|
||||
if (!children.isEmpty()) {
|
||||
commentRepository.deleteAll(children);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将实体转换为DTO
|
||||
*
|
||||
* @param comment 评论实体
|
||||
* @return 评论DTO
|
||||
*/
|
||||
private CommentDTO convertToDTO(Comment comment) {
|
||||
CommentDTO commentDTO = new CommentDTO();
|
||||
BeanUtils.copyProperties(comment, commentDTO);
|
||||
|
||||
// 设置用户信息
|
||||
UserDTO userDTO = new UserDTO();
|
||||
BeanUtils.copyProperties(comment.getUser(), userDTO);
|
||||
|
||||
// 确保头像URL是完整路径
|
||||
if (userDTO.getAvatar() != null && !userDTO.getAvatar().startsWith("http")) {
|
||||
userDTO.setAvatar("http://localhost:8085" + userDTO.getAvatar());
|
||||
}
|
||||
|
||||
commentDTO.setUser(userDTO);
|
||||
|
||||
return commentDTO;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package com.test.musichouduan.service.impl;
|
||||
|
||||
import com.test.musichouduan.exception.BusinessException;
|
||||
import com.test.musichouduan.service.FileService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 文件服务实现类
|
||||
*/
|
||||
@Service
|
||||
public class FileServiceImpl implements FileService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(FileServiceImpl.class);
|
||||
|
||||
@Value("${file.upload-path}")
|
||||
private String uploadPath;
|
||||
|
||||
@Value("${file.access-path}")
|
||||
private String accessPath;
|
||||
|
||||
@Value("${file.allowed-types}")
|
||||
private String allowedTypes;
|
||||
|
||||
@Override
|
||||
public String uploadImage(MultipartFile file) {
|
||||
// 检查文件是否为空
|
||||
if (file.isEmpty()) {
|
||||
throw new BusinessException("上传文件不能为空");
|
||||
}
|
||||
|
||||
// 检查文件类型
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String extension = getFileExtension(originalFilename);
|
||||
List<String> imageTypes = Arrays.asList("jpg", "jpeg", "png", "gif");
|
||||
if (!imageTypes.contains(extension.toLowerCase())) {
|
||||
throw new BusinessException("只能上传jpg、jpeg、png、gif格式的图片");
|
||||
}
|
||||
|
||||
// 上传文件
|
||||
return uploadFile(file, "images");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadMusic(MultipartFile file) {
|
||||
// 检查文件是否为空
|
||||
if (file.isEmpty()) {
|
||||
throw new BusinessException("上传文件不能为空");
|
||||
}
|
||||
|
||||
// 检查文件类型
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String extension = getFileExtension(originalFilename);
|
||||
List<String> musicTypes = Arrays.asList("mp3", "wav");
|
||||
if (!musicTypes.contains(extension.toLowerCase())) {
|
||||
throw new BusinessException("只能上传mp3、wav格式的音乐");
|
||||
}
|
||||
|
||||
// 上传文件
|
||||
return uploadFile(file, "musics");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadLyric(MultipartFile file) {
|
||||
// 检查文件是否为空
|
||||
if (file.isEmpty()) {
|
||||
throw new BusinessException("上传文件不能为空");
|
||||
}
|
||||
|
||||
// 检查文件类型
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String extension = getFileExtension(originalFilename);
|
||||
List<String> lyricTypes = Arrays.asList("lrc", "txt");
|
||||
if (!lyricTypes.contains(extension.toLowerCase())) {
|
||||
throw new BusinessException("只能上传lrc、txt格式的歌词");
|
||||
}
|
||||
|
||||
try {
|
||||
// 读取文件内容
|
||||
byte[] bytes = file.getBytes();
|
||||
String content = new String(bytes, StandardCharsets.UTF_8);
|
||||
return content;
|
||||
} catch (IOException e) {
|
||||
logger.error("读取歌词文件失败", e);
|
||||
throw new BusinessException("读取歌词文件失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteFile(String url) {
|
||||
if (StringUtils.isEmpty(url)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 从URL中提取文件路径
|
||||
String filePath = url.replace(accessPath, "");
|
||||
Path path = Paths.get(uploadPath, filePath);
|
||||
|
||||
try {
|
||||
// 删除文件
|
||||
return Files.deleteIfExists(path);
|
||||
} catch (IOException e) {
|
||||
logger.error("删除文件失败", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param file 文件
|
||||
* @param directory 目录
|
||||
* @return 文件URL
|
||||
*/
|
||||
private String uploadFile(MultipartFile file, String directory) {
|
||||
// 生成文件名
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String extension = getFileExtension(originalFilename);
|
||||
String fileName = UUID.randomUUID().toString() + "." + extension;
|
||||
|
||||
// 创建目录
|
||||
File dir = new File(uploadPath + directory);
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
|
||||
// 保存文件
|
||||
try {
|
||||
Path path = Paths.get(uploadPath, directory, fileName);
|
||||
Files.write(path, file.getBytes());
|
||||
return "http://localhost:8085" + accessPath + directory + "/" + fileName;
|
||||
} catch (IOException e) {
|
||||
logger.error("上传文件失败", e);
|
||||
throw new BusinessException("上传文件失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件扩展名
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @return 扩展名
|
||||
*/
|
||||
private String getFileExtension(String fileName) {
|
||||
if (fileName == null) {
|
||||
return "";
|
||||
}
|
||||
int dotIndex = fileName.lastIndexOf(".");
|
||||
if (dotIndex == -1) {
|
||||
return "";
|
||||
}
|
||||
return fileName.substring(dotIndex + 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,504 @@
|
||||
package com.test.musichouduan.service.impl;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.test.musichouduan.dto.MusicDTO;
|
||||
import com.test.musichouduan.dto.PageResult;
|
||||
import com.test.musichouduan.entity.Music;
|
||||
import com.test.musichouduan.entity.MusicType;
|
||||
import com.test.musichouduan.entity.Singer;
|
||||
import com.test.musichouduan.entity.User;
|
||||
import com.test.musichouduan.entity.UserMusic;
|
||||
import com.test.musichouduan.exception.BusinessException;
|
||||
import com.test.musichouduan.repository.*;
|
||||
import com.test.musichouduan.service.FileService;
|
||||
import com.test.musichouduan.service.MusicService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 音乐服务实现类
|
||||
*/
|
||||
@Service
|
||||
public class MusicServiceImpl implements MusicService {
|
||||
|
||||
@Autowired
|
||||
private MusicRepository musicRepository;
|
||||
|
||||
@Autowired
|
||||
private MusicTypeRepository musicTypeRepository;
|
||||
|
||||
@Autowired
|
||||
private SingerRepository singerRepository;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
private UserMusicRepository userMusicRepository;
|
||||
|
||||
@Autowired
|
||||
private PlayHistoryRepository playHistoryRepository;
|
||||
|
||||
@Autowired
|
||||
private FileService fileService;
|
||||
|
||||
@Override
|
||||
public MusicDTO getMusicById(Long id) {
|
||||
// 查找音乐,如果不存在则返回null
|
||||
return musicRepository.findById(id)
|
||||
.map(this::convertToDTO)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<MusicDTO> getMusicList(Integer page, Integer size, String keyword, Long typeId) {
|
||||
// 构建查询条件
|
||||
Specification<Music> spec = (root, query, cb) -> {
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
|
||||
// 关键字查询
|
||||
if (StringUtils.hasText(keyword)) {
|
||||
predicates.add(cb.like(root.get("name"), "%" + keyword + "%"));
|
||||
}
|
||||
|
||||
// 类型查询
|
||||
if (typeId != null) {
|
||||
predicates.add(cb.isMember(
|
||||
musicTypeRepository.findById(typeId).orElse(null),
|
||||
root.get("types")
|
||||
));
|
||||
}
|
||||
|
||||
return cb.and(predicates.toArray(new Predicate[0]));
|
||||
};
|
||||
|
||||
// 执行查询
|
||||
Page<Music> musicPage = musicRepository.findAll(
|
||||
spec,
|
||||
PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createTime"))
|
||||
);
|
||||
|
||||
// 转换为DTO
|
||||
List<MusicDTO> musicDTOs = musicPage.getContent().stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new PageResult<>(
|
||||
musicDTOs,
|
||||
musicPage.getTotalElements(),
|
||||
page,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<MusicDTO> getMusicListBySinger(Long singerId, Integer page, Integer size) {
|
||||
// 获取歌手
|
||||
Singer singer = singerRepository.findById(singerId)
|
||||
.orElseThrow(() -> new BusinessException("歌手不存在"));
|
||||
|
||||
// 查询歌手的音乐
|
||||
Page<Music> musicPage = musicRepository.findBySingersContaining(
|
||||
singer,
|
||||
PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createTime"))
|
||||
);
|
||||
|
||||
// 转换为DTO
|
||||
List<MusicDTO> musicDTOs = musicPage.getContent().stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new PageResult<>(
|
||||
musicDTOs,
|
||||
musicPage.getTotalElements(),
|
||||
page,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MusicDTO> getLatestMusic(Integer limit) {
|
||||
// 查询最新音乐
|
||||
List<Music> musics = musicRepository.findLatest(PageRequest.of(0, limit));
|
||||
|
||||
// 转换为DTO
|
||||
return musics.stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MusicDTO> getHotMusic(Integer limit) {
|
||||
// 查询热门音乐
|
||||
List<Music> musics = musicRepository.findMostPlayed(PageRequest.of(0, limit));
|
||||
|
||||
// 转换为DTO
|
||||
return musics.stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public MusicDTO addMusic(MusicDTO musicDTO, MultipartFile file, MultipartFile cover) {
|
||||
// 上传音乐文件
|
||||
String musicUrl = fileService.uploadMusic(file);
|
||||
|
||||
// 上传封面
|
||||
String coverUrl = fileService.uploadImage(cover);
|
||||
|
||||
// 创建音乐实体
|
||||
Music music = new Music();
|
||||
BeanUtils.copyProperties(musicDTO, music);
|
||||
music.setUrl(musicUrl);
|
||||
music.setCover(coverUrl);
|
||||
music.setPlayCount(0L);
|
||||
|
||||
// 设置歌词
|
||||
if (StringUtils.hasText(musicDTO.getLyric())) {
|
||||
music.setLyric(musicDTO.getLyric());
|
||||
}
|
||||
|
||||
// 设置音乐类型
|
||||
if (musicDTO.getTypeIds() != null && !musicDTO.getTypeIds().isEmpty()) {
|
||||
Set<MusicType> types = new HashSet<>();
|
||||
for (Long typeId : musicDTO.getTypeIds()) {
|
||||
MusicType type = musicTypeRepository.findById(typeId)
|
||||
.orElseThrow(() -> new BusinessException("音乐类型不存在"));
|
||||
types.add(type);
|
||||
}
|
||||
music.setTypes(types);
|
||||
}
|
||||
|
||||
// 设置歌手
|
||||
if (musicDTO.getSingerIds() != null && !musicDTO.getSingerIds().isEmpty()) {
|
||||
// 如果有歌手ID列表,优先使用
|
||||
Set<Singer> singers = new HashSet<>();
|
||||
for (Long singerId : musicDTO.getSingerIds()) {
|
||||
Singer singer = singerRepository.findById(singerId)
|
||||
.orElseThrow(() -> new BusinessException("歌手不存在"));
|
||||
singers.add(singer);
|
||||
}
|
||||
music.setSingers(singers);
|
||||
} else if (StringUtils.hasText(musicDTO.getSinger())) {
|
||||
// 如果有歌手名称字符串,则处理歌手名称
|
||||
String singerName = musicDTO.getSinger();
|
||||
// 查找是否已存在该歌手
|
||||
Optional<Singer> existingSinger = singerRepository.findByName(singerName);
|
||||
|
||||
if (existingSinger.isPresent()) {
|
||||
// 如果歌手已存在,直接使用
|
||||
Set<Singer> singers = new HashSet<>();
|
||||
singers.add(existingSinger.get());
|
||||
music.setSingers(singers);
|
||||
} else {
|
||||
// 如果歌手不存在,创建新歌手
|
||||
Singer newSinger = new Singer();
|
||||
newSinger.setName(singerName);
|
||||
Singer savedSinger = singerRepository.save(newSinger);
|
||||
|
||||
Set<Singer> singers = new HashSet<>();
|
||||
singers.add(savedSinger);
|
||||
music.setSingers(singers);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存音乐
|
||||
Music savedMusic = musicRepository.save(music);
|
||||
|
||||
return convertToDTO(savedMusic);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public MusicDTO updateMusic(Long id, MusicDTO musicDTO, MultipartFile file, MultipartFile cover, MultipartFile lyric) {
|
||||
// 获取音乐
|
||||
Music music = musicRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("音乐不存在"));
|
||||
|
||||
// 如果有新的音乐文件,则上传并更新URL
|
||||
if (file != null && !file.isEmpty()) {
|
||||
// 删除旧文件
|
||||
if (music.getUrl() != null) {
|
||||
fileService.deleteFile(music.getUrl());
|
||||
}
|
||||
|
||||
// 上传新文件
|
||||
String musicUrl = fileService.uploadMusic(file);
|
||||
music.setUrl(musicUrl);
|
||||
}
|
||||
|
||||
// 如果有新的封面,则上传并更新URL
|
||||
if (cover != null && !cover.isEmpty()) {
|
||||
// 删除旧封面
|
||||
if (music.getCover() != null) {
|
||||
fileService.deleteFile(music.getCover());
|
||||
}
|
||||
|
||||
// 上传新封面
|
||||
String coverUrl = fileService.uploadImage(cover);
|
||||
music.setCover(coverUrl);
|
||||
}
|
||||
|
||||
// 如果有新的歌词文件,则读取歌词内容
|
||||
if (lyric != null && !lyric.isEmpty()) {
|
||||
try {
|
||||
String lyricContent = new String(lyric.getBytes(), "UTF-8");
|
||||
music.setLyric(lyricContent);
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException("读取歌词文件失败");
|
||||
}
|
||||
} else if (StringUtils.hasText(musicDTO.getLyric())) {
|
||||
// 如果没有歌词文件,但有歌词内容,则直接使用歌词内容
|
||||
music.setLyric(musicDTO.getLyric());
|
||||
}
|
||||
|
||||
// 更新音乐信息
|
||||
music.setName(musicDTO.getName());
|
||||
music.setAlbum(musicDTO.getAlbum());
|
||||
music.setDuration(musicDTO.getDuration());
|
||||
|
||||
// 更新音乐类型
|
||||
if (musicDTO.getTypeIds() != null) {
|
||||
Set<MusicType> types = new HashSet<>();
|
||||
for (Long typeId : musicDTO.getTypeIds()) {
|
||||
MusicType type = musicTypeRepository.findById(typeId)
|
||||
.orElseThrow(() -> new BusinessException("音乐类型不存在"));
|
||||
types.add(type);
|
||||
}
|
||||
music.setTypes(types);
|
||||
}
|
||||
|
||||
// 更新歌手
|
||||
if (musicDTO.getSingerIds() != null && !musicDTO.getSingerIds().isEmpty()) {
|
||||
// 如果有歌手ID列表,优先使用
|
||||
Set<Singer> singers = new HashSet<>();
|
||||
for (Long singerId : musicDTO.getSingerIds()) {
|
||||
Singer singer = singerRepository.findById(singerId)
|
||||
.orElseThrow(() -> new BusinessException("歌手不存在"));
|
||||
singers.add(singer);
|
||||
}
|
||||
music.setSingers(singers);
|
||||
} else if (StringUtils.hasText(musicDTO.getSinger())) {
|
||||
// 如果有歌手名称字符串,则处理歌手名称
|
||||
String singerName = musicDTO.getSinger();
|
||||
// 查找是否已存在该歌手
|
||||
Optional<Singer> existingSinger = singerRepository.findByName(singerName);
|
||||
|
||||
if (existingSinger.isPresent()) {
|
||||
// 如果歌手已存在,直接使用
|
||||
Set<Singer> singers = new HashSet<>();
|
||||
singers.add(existingSinger.get());
|
||||
music.setSingers(singers);
|
||||
} else {
|
||||
// 如果歌手不存在,创建新歌手
|
||||
Singer newSinger = new Singer();
|
||||
newSinger.setName(singerName);
|
||||
Singer savedSinger = singerRepository.save(newSinger);
|
||||
|
||||
Set<Singer> singers = new HashSet<>();
|
||||
singers.add(savedSinger);
|
||||
music.setSingers(singers);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存音乐
|
||||
Music updatedMusic = musicRepository.save(music);
|
||||
|
||||
return convertToDTO(updatedMusic);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean deleteMusic(Long id) {
|
||||
// 获取音乐
|
||||
Music music = musicRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("音乐不存在"));
|
||||
|
||||
// 删除音乐文件
|
||||
if (music.getUrl() != null) {
|
||||
fileService.deleteFile(music.getUrl());
|
||||
}
|
||||
|
||||
// 删除封面
|
||||
if (music.getCover() != null) {
|
||||
fileService.deleteFile(music.getCover());
|
||||
}
|
||||
|
||||
// 删除音乐
|
||||
musicRepository.delete(music);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean toggleCollectMusic(Long id, Long userId) {
|
||||
// 获取用户
|
||||
Optional<User> userOpt = userRepository.findById(userId);
|
||||
if (!userOpt.isPresent()) {
|
||||
// 如果用户不存在,返回false
|
||||
return false;
|
||||
}
|
||||
User user = userOpt.get();
|
||||
|
||||
// 获取音乐
|
||||
Optional<Music> musicOpt = musicRepository.findById(id);
|
||||
if (!musicOpt.isPresent()) {
|
||||
// 如果音乐不存在,返回false
|
||||
return false;
|
||||
}
|
||||
Music music = musicOpt.get();
|
||||
|
||||
// 检查是否已收藏
|
||||
boolean isCollected = userMusicRepository.existsByUserAndMusic(user, music);
|
||||
|
||||
if (isCollected) {
|
||||
// 如果已收藏,则取消收藏
|
||||
userMusicRepository.uncollectMusic(user.getId(), music.getId());
|
||||
} else {
|
||||
// 如果未收藏,则收藏
|
||||
userMusicRepository.collectMusic(user.getId(), music.getId());
|
||||
}
|
||||
|
||||
return !isCollected; // 返回当前收藏状态,true表示已收藏,false表示未收藏
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<MusicDTO> getCollectedMusic(Long userId, Integer page, Integer size) {
|
||||
// 获取用户
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new BusinessException("用户不存在"));
|
||||
|
||||
// 获取用户收藏的音乐
|
||||
Page<UserMusic> userMusicPage = userMusicRepository.findCollectedMusic(
|
||||
user.getId(),
|
||||
PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "collectTime"))
|
||||
);
|
||||
|
||||
// 转换为DTO
|
||||
List<MusicDTO> musicDTOs = userMusicPage.getContent().stream()
|
||||
.map(userMusic -> {
|
||||
MusicDTO musicDTO = convertToDTO(userMusic.getMusic());
|
||||
// 设置收藏时间
|
||||
musicDTO.setCollectTime(userMusic.getCollectTime());
|
||||
return musicDTO;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new PageResult<>(
|
||||
musicDTOs,
|
||||
userMusicPage.getTotalElements(),
|
||||
page,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public MusicDTO playMusic(Long id) {
|
||||
// 获取音乐
|
||||
Optional<Music> musicOpt = musicRepository.findById(id);
|
||||
if (!musicOpt.isPresent()) {
|
||||
// 如果音乐不存在,返回null
|
||||
return null;
|
||||
}
|
||||
|
||||
Music music = musicOpt.get();
|
||||
|
||||
// 增加播放次数
|
||||
music.setPlayCount(music.getPlayCount() + 1);
|
||||
musicRepository.save(music);
|
||||
|
||||
// 如果用户已登录,记录播放历史
|
||||
if (StpUtil.isLogin()) {
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
User user = userRepository.findById(userId).orElse(null);
|
||||
if (user != null) {
|
||||
playHistoryRepository.recordPlayHistory(user.getId(), music.getId());
|
||||
}
|
||||
}
|
||||
|
||||
return convertToDTO(music);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Music getById(Long id) {
|
||||
return musicRepository.findById(id).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将实体转换为DTO
|
||||
*
|
||||
* @param music 音乐实体
|
||||
* @return 音乐DTO
|
||||
*/
|
||||
private MusicDTO convertToDTO(Music music) {
|
||||
MusicDTO musicDTO = new MusicDTO();
|
||||
BeanUtils.copyProperties(music, musicDTO);
|
||||
|
||||
// 确保专辑字段正确复制
|
||||
musicDTO.setAlbum(music.getAlbum());
|
||||
|
||||
// 设置类型ID列表
|
||||
if (music.getTypes() != null) {
|
||||
List<Long> typeIds = music.getTypes().stream()
|
||||
.map(MusicType::getId)
|
||||
.collect(Collectors.toList());
|
||||
musicDTO.setTypeIds(typeIds);
|
||||
|
||||
// 设置类型名称列表
|
||||
List<String> typeNames = music.getTypes().stream()
|
||||
.map(MusicType::getName)
|
||||
.collect(Collectors.toList());
|
||||
musicDTO.setTypeNames(typeNames);
|
||||
}
|
||||
|
||||
// 设置歌手ID列表
|
||||
if (music.getSingers() != null) {
|
||||
List<Long> singerIds = music.getSingers().stream()
|
||||
.map(Singer::getId)
|
||||
.collect(Collectors.toList());
|
||||
musicDTO.setSingerIds(singerIds);
|
||||
|
||||
// 设置歌手名称列表
|
||||
List<String> singerNames = music.getSingers().stream()
|
||||
.map(Singer::getName)
|
||||
.collect(Collectors.toList());
|
||||
musicDTO.setSingerNames(singerNames);
|
||||
|
||||
// 设置歌手名称字符串(用于前端显示)
|
||||
if (!singerNames.isEmpty()) {
|
||||
musicDTO.setSinger(String.join(", ", singerNames));
|
||||
}
|
||||
}
|
||||
|
||||
// 检查当前登录用户是否已收藏该音乐
|
||||
if (StpUtil.isLogin()) {
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
boolean isCollected = userMusicRepository.existsByUserIdAndMusicId(userId, music.getId());
|
||||
musicDTO.setCollected(isCollected);
|
||||
} else {
|
||||
musicDTO.setCollected(false);
|
||||
}
|
||||
|
||||
return musicDTO;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
package com.test.musichouduan.service.impl;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.test.musichouduan.dto.MusicDTO;
|
||||
import com.test.musichouduan.dto.PageResult;
|
||||
import com.test.musichouduan.dto.PlaylistDTO;
|
||||
import com.test.musichouduan.dto.UserDTO;
|
||||
import com.test.musichouduan.entity.Music;
|
||||
import com.test.musichouduan.entity.Playlist;
|
||||
import com.test.musichouduan.entity.PlaylistType;
|
||||
import com.test.musichouduan.entity.User;
|
||||
import com.test.musichouduan.exception.BusinessException;
|
||||
import com.test.musichouduan.repository.*;
|
||||
import com.test.musichouduan.service.FileService;
|
||||
import com.test.musichouduan.service.PlaylistService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 歌单服务实现类
|
||||
*/
|
||||
@Service
|
||||
public class PlaylistServiceImpl implements PlaylistService {
|
||||
|
||||
@Autowired
|
||||
private PlaylistRepository playlistRepository;
|
||||
|
||||
@Autowired
|
||||
private PlaylistTypeRepository playlistTypeRepository;
|
||||
|
||||
@Autowired
|
||||
private MusicRepository musicRepository;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
private UserPlaylistRepository userPlaylistRepository;
|
||||
|
||||
@Autowired
|
||||
private PlayHistoryRepository playHistoryRepository;
|
||||
|
||||
@Autowired
|
||||
private FileService fileService;
|
||||
|
||||
@Override
|
||||
public PlaylistDTO getPlaylistById(Long id) {
|
||||
Playlist playlist = playlistRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("歌单不存在"));
|
||||
return convertToDTO(playlist);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<PlaylistDTO> getPlaylistList(Integer page, Integer size, String keyword, Long typeId) {
|
||||
// 构建查询条件
|
||||
Specification<Playlist> spec = (root, query, cb) -> {
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
|
||||
// 关键字查询
|
||||
if (StringUtils.hasText(keyword)) {
|
||||
predicates.add(cb.like(root.get("name"), "%" + keyword + "%"));
|
||||
}
|
||||
|
||||
// 类型查询
|
||||
if (typeId != null) {
|
||||
predicates.add(cb.equal(root.get("type").get("id"), typeId));
|
||||
}
|
||||
|
||||
return cb.and(predicates.toArray(new Predicate[0]));
|
||||
};
|
||||
|
||||
// 执行查询
|
||||
Page<Playlist> playlistPage = playlistRepository.findAll(
|
||||
spec,
|
||||
PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createTime"))
|
||||
);
|
||||
|
||||
// 转换为DTO
|
||||
List<PlaylistDTO> playlistDTOs = playlistPage.getContent().stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new PageResult<>(
|
||||
playlistDTOs,
|
||||
playlistPage.getTotalElements(),
|
||||
page,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PlaylistDTO> getLatestPlaylist(Integer limit) {
|
||||
// 查询最新歌单
|
||||
List<Playlist> playlists = playlistRepository.findLatest(PageRequest.of(0, limit));
|
||||
|
||||
// 转换为DTO
|
||||
return playlists.stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PlaylistDTO> getHotPlaylist(Integer limit) {
|
||||
// 查询热门歌单
|
||||
List<Playlist> playlists = playlistRepository.findMostPlayed(PageRequest.of(0, limit));
|
||||
|
||||
// 转换为DTO
|
||||
return playlists.stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<PlaylistDTO> getOfficialPlaylist(Integer page, Integer size) {
|
||||
// 查询官方歌单
|
||||
Page<Playlist> playlistPage = playlistRepository.findByIsOfficial(
|
||||
1,
|
||||
PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createTime"))
|
||||
);
|
||||
|
||||
// 转换为DTO
|
||||
List<PlaylistDTO> playlistDTOs = playlistPage.getContent().stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new PageResult<>(
|
||||
playlistDTOs,
|
||||
playlistPage.getTotalElements(),
|
||||
page,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public PlaylistDTO addPlaylist(PlaylistDTO playlistDTO, MultipartFile cover) {
|
||||
// 获取当前登录用户
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new BusinessException("用户不存在"));
|
||||
|
||||
// 上传封面
|
||||
String coverUrl = null;
|
||||
if (cover != null && !cover.isEmpty()) {
|
||||
coverUrl = fileService.uploadImage(cover);
|
||||
}
|
||||
|
||||
// 创建歌单实体
|
||||
Playlist playlist = new Playlist();
|
||||
playlist.setName(playlistDTO.getName());
|
||||
playlist.setDescription(playlistDTO.getDescription());
|
||||
playlist.setCover(coverUrl);
|
||||
playlist.setCreator(user);
|
||||
playlist.setPlayCount(0L);
|
||||
playlist.setIsOfficial(0); // 默认为非官方歌单
|
||||
|
||||
// 设置歌单类型
|
||||
if (playlistDTO.getType() != null && playlistDTO.getType().getId() != null) {
|
||||
PlaylistType type = playlistTypeRepository.findById(playlistDTO.getType().getId())
|
||||
.orElseThrow(() -> new BusinessException("歌单类型不存在"));
|
||||
playlist.setType(type);
|
||||
}
|
||||
|
||||
// 保存歌单
|
||||
Playlist savedPlaylist = playlistRepository.save(playlist);
|
||||
|
||||
return convertToDTO(savedPlaylist);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public PlaylistDTO updatePlaylist(Long id, PlaylistDTO playlistDTO, MultipartFile cover) {
|
||||
// 获取歌单
|
||||
Playlist playlist = playlistRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("歌单不存在"));
|
||||
|
||||
// 获取当前登录用户
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
|
||||
// 检查是否是歌单创建者或管理员
|
||||
if (!playlist.getCreator().getId().equals(userId) && !StpUtil.hasRole("admin")) {
|
||||
throw new BusinessException("无权修改该歌单");
|
||||
}
|
||||
|
||||
// 如果有新的封面,则上传并更新URL
|
||||
if (cover != null && !cover.isEmpty()) {
|
||||
// 删除旧封面
|
||||
if (playlist.getCover() != null) {
|
||||
fileService.deleteFile(playlist.getCover());
|
||||
}
|
||||
|
||||
// 上传新封面
|
||||
String coverUrl = fileService.uploadImage(cover);
|
||||
playlist.setCover(coverUrl);
|
||||
}
|
||||
|
||||
// 更新歌单信息
|
||||
playlist.setName(playlistDTO.getName());
|
||||
playlist.setDescription(playlistDTO.getDescription());
|
||||
|
||||
// 更新是否官方字段
|
||||
System.out.println("接收到的isOfficial值: " + playlistDTO.getIsOfficial());
|
||||
playlist.setIsOfficial(playlistDTO.getIsOfficial());
|
||||
System.out.println("更新后的歌单isOfficial值: " + playlist.getIsOfficial());
|
||||
|
||||
// 更新歌单类型
|
||||
if (playlistDTO.getType() != null && playlistDTO.getType().getId() != null) {
|
||||
PlaylistType type = playlistTypeRepository.findById(playlistDTO.getType().getId())
|
||||
.orElseThrow(() -> new BusinessException("歌单类型不存在"));
|
||||
playlist.setType(type);
|
||||
}
|
||||
|
||||
// 保存歌单
|
||||
Playlist updatedPlaylist = playlistRepository.save(playlist);
|
||||
|
||||
return convertToDTO(updatedPlaylist);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean deletePlaylist(Long id) {
|
||||
// 获取歌单
|
||||
Playlist playlist = playlistRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("歌单不存在"));
|
||||
|
||||
// 获取当前登录用户
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
|
||||
// 检查是否是歌单创建者或管理员
|
||||
if (!playlist.getCreator().getId().equals(userId) && !StpUtil.hasRole("admin")) {
|
||||
throw new BusinessException("无权删除该歌单");
|
||||
}
|
||||
|
||||
// 删除封面
|
||||
if (playlist.getCover() != null) {
|
||||
fileService.deleteFile(playlist.getCover());
|
||||
}
|
||||
|
||||
// 删除歌单
|
||||
playlistRepository.delete(playlist);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean collectPlaylist(Long id) {
|
||||
// 获取当前登录用户
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new BusinessException("用户不存在"));
|
||||
|
||||
// 获取歌单
|
||||
Playlist playlist = playlistRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("歌单不存在"));
|
||||
|
||||
// 检查是否已收藏
|
||||
if (userPlaylistRepository.existsByUserAndPlaylist(user, playlist)) {
|
||||
throw new BusinessException("已收藏该歌单");
|
||||
}
|
||||
|
||||
// 收藏歌单
|
||||
userPlaylistRepository.collectPlaylist(user.getId(), playlist.getId());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean uncollectPlaylist(Long id) {
|
||||
// 获取当前登录用户
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new BusinessException("用户不存在"));
|
||||
|
||||
// 获取歌单
|
||||
Playlist playlist = playlistRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("歌单不存在"));
|
||||
|
||||
// 检查是否已收藏
|
||||
if (!userPlaylistRepository.existsByUserAndPlaylist(user, playlist)) {
|
||||
throw new BusinessException("未收藏该歌单");
|
||||
}
|
||||
|
||||
// 取消收藏歌单
|
||||
userPlaylistRepository.uncollectPlaylist(user.getId(), playlist.getId());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<PlaylistDTO> getCollectedPlaylist(Integer page, Integer size) {
|
||||
// 获取当前登录用户
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
|
||||
// 获取用户收藏的歌单
|
||||
Page<Playlist> playlistPage = userPlaylistRepository.findCollectedPlaylist(
|
||||
userId,
|
||||
PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "collectTime"))
|
||||
);
|
||||
|
||||
// 转换为DTO
|
||||
List<PlaylistDTO> playlistDTOs = playlistPage.getContent().stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new PageResult<>(
|
||||
playlistDTOs,
|
||||
playlistPage.getTotalElements(),
|
||||
page,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<PlaylistDTO> getUserPlaylist(Integer page, Integer size) {
|
||||
// 获取当前登录用户
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new BusinessException("用户不存在"));
|
||||
|
||||
// 获取用户创建的歌单
|
||||
Page<Playlist> playlistPage = playlistRepository.findByCreator(
|
||||
user,
|
||||
PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createTime"))
|
||||
);
|
||||
|
||||
// 转换为DTO
|
||||
List<PlaylistDTO> playlistDTOs = playlistPage.getContent().stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new PageResult<>(
|
||||
playlistDTOs,
|
||||
playlistPage.getTotalElements(),
|
||||
page,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean addMusicToPlaylist(Long id, Long musicId) {
|
||||
// 获取歌单
|
||||
Playlist playlist = playlistRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("歌单不存在"));
|
||||
|
||||
// 获取当前登录用户
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
|
||||
// 检查是否是歌单创建者
|
||||
if (!playlist.getCreator().getId().equals(userId)) {
|
||||
throw new BusinessException("无权修改该歌单");
|
||||
}
|
||||
|
||||
// 获取音乐
|
||||
Music music = musicRepository.findById(musicId)
|
||||
.orElseThrow(() -> new BusinessException("音乐不存在"));
|
||||
|
||||
// 检查音乐是否已在歌单中
|
||||
if (playlist.getMusics().contains(music)) {
|
||||
throw new BusinessException("该音乐已在歌单中");
|
||||
}
|
||||
|
||||
// 添加音乐到歌单
|
||||
playlist.getMusics().add(music);
|
||||
playlistRepository.save(playlist);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean removeMusicFromPlaylist(Long id, Long musicId) {
|
||||
// 获取歌单
|
||||
Playlist playlist = playlistRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("歌单不存在"));
|
||||
|
||||
// 获取当前登录用户
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
|
||||
// 检查是否是歌单创建者
|
||||
if (!playlist.getCreator().getId().equals(userId)) {
|
||||
throw new BusinessException("无权修改该歌单");
|
||||
}
|
||||
|
||||
// 获取音乐
|
||||
Music music = musicRepository.findById(musicId)
|
||||
.orElseThrow(() -> new BusinessException("音乐不存在"));
|
||||
|
||||
// 检查音乐是否在歌单中
|
||||
if (!playlist.getMusics().contains(music)) {
|
||||
throw new BusinessException("该音乐不在歌单中");
|
||||
}
|
||||
|
||||
// 从歌单中移除音乐
|
||||
playlist.getMusics().remove(music);
|
||||
playlistRepository.save(playlist);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public PlaylistDTO playPlaylist(Long id) {
|
||||
// 获取歌单
|
||||
Playlist playlist = playlistRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("歌单不存在"));
|
||||
|
||||
// 增加播放次数
|
||||
playlist.setPlayCount(playlist.getPlayCount() + 1);
|
||||
playlistRepository.save(playlist);
|
||||
|
||||
return convertToDTO(playlist);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Playlist getById(Long id) {
|
||||
return playlistRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("歌单不存在"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将实体转换为DTO
|
||||
*
|
||||
* @param playlist 歌单实体
|
||||
* @return 歌单DTO
|
||||
*/
|
||||
private PlaylistDTO convertToDTO(Playlist playlist) {
|
||||
PlaylistDTO playlistDTO = new PlaylistDTO();
|
||||
BeanUtils.copyProperties(playlist, playlistDTO);
|
||||
|
||||
// 设置创建者信息
|
||||
if (playlist.getCreator() != null) {
|
||||
UserDTO creatorDTO = new UserDTO();
|
||||
BeanUtils.copyProperties(playlist.getCreator(), creatorDTO);
|
||||
playlistDTO.setCreator(creatorDTO);
|
||||
}
|
||||
|
||||
// 设置音乐列表
|
||||
if (playlist.getMusics() != null && !playlist.getMusics().isEmpty()) {
|
||||
List<MusicDTO> musicDTOs = playlist.getMusics().stream()
|
||||
.map(music -> {
|
||||
MusicDTO musicDTO = new MusicDTO();
|
||||
BeanUtils.copyProperties(music, musicDTO);
|
||||
return musicDTO;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
playlistDTO.setMusics(musicDTOs);
|
||||
playlistDTO.setMusicCount(musicDTOs.size());
|
||||
} else {
|
||||
playlistDTO.setMusicCount(0);
|
||||
}
|
||||
|
||||
// 检查当前登录用户是否已收藏该歌单
|
||||
if (StpUtil.isLogin()) {
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
boolean isCollected = userPlaylistRepository.existsByUserIdAndPlaylistId(userId, playlist.getId());
|
||||
playlistDTO.setIsCollected(isCollected);
|
||||
} else {
|
||||
playlistDTO.setIsCollected(false);
|
||||
}
|
||||
|
||||
return playlistDTO;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,389 @@
|
||||
package com.test.musichouduan.service.impl;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.test.musichouduan.dto.PageResult;
|
||||
import com.test.musichouduan.dto.SingerDTO;
|
||||
import com.test.musichouduan.dto.SingerTypeDTO;
|
||||
import com.test.musichouduan.dto.MusicDTO;
|
||||
import com.test.musichouduan.entity.Singer;
|
||||
import com.test.musichouduan.entity.SingerType;
|
||||
import com.test.musichouduan.entity.User;
|
||||
import com.test.musichouduan.entity.Music;
|
||||
import com.test.musichouduan.exception.BusinessException;
|
||||
import com.test.musichouduan.repository.MusicRepository;
|
||||
import com.test.musichouduan.repository.SingerRepository;
|
||||
import com.test.musichouduan.repository.SingerTypeRepository;
|
||||
import com.test.musichouduan.repository.UserRepository;
|
||||
import com.test.musichouduan.repository.UserSingerRepository;
|
||||
import com.test.musichouduan.service.FileService;
|
||||
import com.test.musichouduan.service.SingerService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 歌手服务实现类
|
||||
*/
|
||||
@Service
|
||||
public class SingerServiceImpl implements SingerService {
|
||||
|
||||
@Autowired
|
||||
private SingerRepository singerRepository;
|
||||
|
||||
@Autowired
|
||||
private SingerTypeRepository singerTypeRepository;
|
||||
|
||||
@Autowired
|
||||
private MusicRepository musicRepository;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
private UserSingerRepository userSingerRepository;
|
||||
|
||||
@Autowired
|
||||
private FileService fileService;
|
||||
|
||||
@Override
|
||||
public SingerDTO getSingerById(Long id) {
|
||||
Singer singer = singerRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("歌手不存在"));
|
||||
return convertToDTO(singer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<SingerDTO> getSingerList(Integer page, Integer size, String keyword, Long typeId) {
|
||||
// 构建查询条件
|
||||
Specification<Singer> spec = (root, query, cb) -> {
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
|
||||
// 关键字查询
|
||||
if (StringUtils.hasText(keyword)) {
|
||||
predicates.add(cb.like(root.get("name"), "%" + keyword + "%"));
|
||||
}
|
||||
|
||||
// 类型查询
|
||||
if (typeId != null) {
|
||||
predicates.add(cb.equal(root.get("type").get("id"), typeId));
|
||||
}
|
||||
|
||||
return cb.and(predicates.toArray(new Predicate[0]));
|
||||
};
|
||||
|
||||
// 执行查询
|
||||
Page<Singer> singerPage = singerRepository.findAll(
|
||||
spec,
|
||||
PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createTime"))
|
||||
);
|
||||
|
||||
// 转换为DTO
|
||||
List<SingerDTO> singerDTOs = singerPage.getContent().stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new PageResult<>(
|
||||
singerDTOs,
|
||||
singerPage.getTotalElements(),
|
||||
page,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SingerDTO> getLatestSinger(Integer limit) {
|
||||
// 查询最新歌手
|
||||
List<Singer> singers = singerRepository.findLatest(PageRequest.of(0, limit));
|
||||
|
||||
// 转换为DTO
|
||||
return singers.stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SingerDTO> getHotSinger(Integer limit) {
|
||||
// 查询热门歌手
|
||||
List<Singer> singers = singerRepository.findHotSingers(PageRequest.of(0, limit));
|
||||
|
||||
// 转换为DTO
|
||||
return singers.stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public SingerDTO addSinger(SingerDTO singerDTO, MultipartFile avatar) {
|
||||
// 检查歌手名称是否已存在
|
||||
if (singerRepository.findByName(singerDTO.getName()).isPresent()) {
|
||||
throw new BusinessException("歌手名称已存在");
|
||||
}
|
||||
|
||||
// 上传头像
|
||||
String avatarUrl = null;
|
||||
if (avatar != null && !avatar.isEmpty()) {
|
||||
avatarUrl = fileService.uploadImage(avatar);
|
||||
}
|
||||
|
||||
// 创建歌手实体
|
||||
Singer singer = new Singer();
|
||||
singer.setName(singerDTO.getName());
|
||||
singer.setAvatar(avatarUrl);
|
||||
singer.setIntroduction(singerDTO.getIntroduction());
|
||||
|
||||
// 设置歌手类型
|
||||
if (singerDTO.getType() != null && singerDTO.getType().getId() != null) {
|
||||
SingerType type = singerTypeRepository.findById(singerDTO.getType().getId())
|
||||
.orElseThrow(() -> new BusinessException("歌手类型不存在"));
|
||||
singer.setType(type);
|
||||
}
|
||||
|
||||
// 保存歌手
|
||||
Singer savedSinger = singerRepository.save(singer);
|
||||
|
||||
return convertToDTO(savedSinger);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public SingerDTO updateSinger(Long id, SingerDTO singerDTO, MultipartFile avatar) {
|
||||
// 获取歌手
|
||||
Singer singer = singerRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("歌手不存在"));
|
||||
|
||||
// 检查歌手名称是否已存在
|
||||
if (!singer.getName().equals(singerDTO.getName()) &&
|
||||
singerRepository.findByName(singerDTO.getName()).isPresent()) {
|
||||
throw new BusinessException("歌手名称已存在");
|
||||
}
|
||||
|
||||
// 如果有新的头像,则上传并更新URL
|
||||
if (avatar != null && !avatar.isEmpty()) {
|
||||
// 删除旧头像
|
||||
if (singer.getAvatar() != null) {
|
||||
fileService.deleteFile(singer.getAvatar());
|
||||
}
|
||||
|
||||
// 上传新头像
|
||||
String avatarUrl = fileService.uploadImage(avatar);
|
||||
singer.setAvatar(avatarUrl);
|
||||
}
|
||||
|
||||
// 更新歌手信息
|
||||
singer.setName(singerDTO.getName());
|
||||
singer.setIntroduction(singerDTO.getIntroduction());
|
||||
|
||||
// 更新歌手类型
|
||||
if (singerDTO.getType() != null && singerDTO.getType().getId() != null) {
|
||||
SingerType type = singerTypeRepository.findById(singerDTO.getType().getId())
|
||||
.orElseThrow(() -> new BusinessException("歌手类型不存在"));
|
||||
singer.setType(type);
|
||||
}
|
||||
|
||||
// 保存歌手
|
||||
Singer updatedSinger = singerRepository.save(singer);
|
||||
|
||||
return convertToDTO(updatedSinger);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean deleteSinger(Long id) {
|
||||
// 获取歌手
|
||||
Singer singer = singerRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("歌手不存在"));
|
||||
|
||||
// 删除头像
|
||||
if (singer.getAvatar() != null) {
|
||||
fileService.deleteFile(singer.getAvatar());
|
||||
}
|
||||
|
||||
// 删除歌手
|
||||
singerRepository.delete(singer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean collectSinger(Long id) {
|
||||
// 获取当前登录用户
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new BusinessException("用户不存在"));
|
||||
|
||||
// 获取歌手
|
||||
Singer singer = singerRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("歌手不存在"));
|
||||
|
||||
// 检查是否已收藏
|
||||
if (userSingerRepository.existsByUserAndSinger(user, singer)) {
|
||||
throw new BusinessException("已收藏该歌手");
|
||||
}
|
||||
|
||||
// 收藏歌手
|
||||
userSingerRepository.collectSinger(user.getId(), singer.getId());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean uncollectSinger(Long id) {
|
||||
// 获取当前登录用户
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new BusinessException("用户不存在"));
|
||||
|
||||
// 获取歌手
|
||||
Singer singer = singerRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("歌手不存在"));
|
||||
|
||||
// 检查是否已收藏
|
||||
if (!userSingerRepository.existsByUserAndSinger(user, singer)) {
|
||||
throw new BusinessException("未收藏该歌手");
|
||||
}
|
||||
|
||||
// 取消收藏歌手
|
||||
userSingerRepository.uncollectSinger(user.getId(), singer.getId());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<SingerDTO> getCollectedSinger(Integer page, Integer size) {
|
||||
// 获取当前登录用户
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
|
||||
// 获取用户收藏的歌手
|
||||
Page<Singer> singerPage = userSingerRepository.findCollectedSinger(
|
||||
userId,
|
||||
PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "collectTime"))
|
||||
);
|
||||
|
||||
// 转换为DTO
|
||||
List<SingerDTO> singerDTOs = singerPage.getContent().stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new PageResult<>(
|
||||
singerDTOs,
|
||||
singerPage.getTotalElements(),
|
||||
page,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean addMusicToSinger(Long singerId, Long musicId) {
|
||||
// 获取歌手
|
||||
Singer singer = singerRepository.findById(singerId)
|
||||
.orElseThrow(() -> new BusinessException("歌手不存在"));
|
||||
|
||||
// 获取音乐
|
||||
Music music = musicRepository.findById(musicId)
|
||||
.orElseThrow(() -> new BusinessException("音乐不存在"));
|
||||
|
||||
// 检查是否已关联(双向检查)
|
||||
if (singer.getMusics().contains(music) || music.getSingers().contains(singer)) {
|
||||
throw new BusinessException("该音乐已关联到歌手");
|
||||
}
|
||||
|
||||
// 添加关联并保存
|
||||
singer.getMusics().add(music);
|
||||
singerRepository.save(singer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean removeMusicFromSinger(Long singerId, Long musicId) {
|
||||
// 获取歌手
|
||||
Singer singer = singerRepository.findById(singerId)
|
||||
.orElseThrow(() -> new BusinessException("歌手不存在"));
|
||||
|
||||
// 获取音乐
|
||||
Music music = musicRepository.findById(musicId)
|
||||
.orElseThrow(() -> new BusinessException("音乐不存在"));
|
||||
|
||||
// 检查是否已关联
|
||||
if (!singer.getMusics().contains(music)) {
|
||||
throw new BusinessException("该音乐未关联到歌手");
|
||||
}
|
||||
|
||||
// 移除关联
|
||||
singer.getMusics().remove(music);
|
||||
singerRepository.save(singer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Singer getById(Long id) {
|
||||
return singerRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("歌手不存在"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将实体转换为DTO
|
||||
*
|
||||
* @param singer 歌手实体
|
||||
* @return 歌手DTO
|
||||
*/
|
||||
private SingerDTO convertToDTO(Singer singer) {
|
||||
SingerDTO singerDTO = new SingerDTO();
|
||||
BeanUtils.copyProperties(singer, singerDTO);
|
||||
|
||||
// 设置类型信息
|
||||
if (singer.getType() != null) {
|
||||
SingerTypeDTO typeDTO = new SingerTypeDTO();
|
||||
BeanUtils.copyProperties(singer.getType(), typeDTO);
|
||||
singerDTO.setType(typeDTO);
|
||||
}
|
||||
|
||||
// 设置音乐数量
|
||||
Long musicCount = musicRepository.countBySingersContaining(singer);
|
||||
singerDTO.setMusicCount(musicCount);
|
||||
|
||||
// 设置粉丝数量
|
||||
Long fansCount = userSingerRepository.count();
|
||||
singerDTO.setFansCount(fansCount);
|
||||
|
||||
// 检查当前登录用户是否已收藏该歌手
|
||||
if (StpUtil.isLogin()) {
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
boolean isCollected = userSingerRepository.existsByUserIdAndSingerId(userId, singer.getId());
|
||||
singerDTO.setIsCollected(isCollected);
|
||||
} else {
|
||||
singerDTO.setIsCollected(false);
|
||||
}
|
||||
|
||||
// 设置关联音乐列表
|
||||
if (singer.getMusics() != null) {
|
||||
singerDTO.setMusics(singer.getMusics().stream()
|
||||
.map(music -> {
|
||||
MusicDTO musicDTO = new MusicDTO();
|
||||
BeanUtils.copyProperties(music, musicDTO);
|
||||
return musicDTO;
|
||||
})
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
return singerDTO;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package com.test.musichouduan.service.impl;
|
||||
|
||||
import com.test.musichouduan.dto.MusicDTO;
|
||||
import com.test.musichouduan.dto.MusicTypeDTO;
|
||||
import com.test.musichouduan.dto.UserDTO;
|
||||
import com.test.musichouduan.entity.Music;
|
||||
import com.test.musichouduan.entity.MusicType;
|
||||
import com.test.musichouduan.entity.User;
|
||||
import com.test.musichouduan.repository.*;
|
||||
import com.test.musichouduan.service.StatService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 统计服务实现类
|
||||
*/
|
||||
@Service
|
||||
public class StatServiceImpl implements StatService {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
private MusicRepository musicRepository;
|
||||
|
||||
@Autowired
|
||||
private PlaylistRepository playlistRepository;
|
||||
|
||||
@Autowired
|
||||
private SingerRepository singerRepository;
|
||||
|
||||
@Autowired
|
||||
private MusicTypeRepository musicTypeRepository;
|
||||
|
||||
@Autowired
|
||||
private PlayHistoryRepository playHistoryRepository;
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getTotalStats() {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
stats.put("userCount", userRepository.count());
|
||||
stats.put("musicCount", musicRepository.count());
|
||||
stats.put("playlistCount", playlistRepository.count());
|
||||
stats.put("singerCount", singerRepository.count());
|
||||
|
||||
// 计算总播放量
|
||||
long totalPlayCount = 0;
|
||||
List<Music> allMusic = musicRepository.findAll();
|
||||
for (Music music : allMusic) {
|
||||
totalPlayCount += music.getPlayCount() != null ? music.getPlayCount() : 0;
|
||||
}
|
||||
stats.put("playCount", totalPlayCount);
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getUserGrowthTrend(Integer days) {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
List<String> dateList = new ArrayList<>();
|
||||
List<Long> countList = new ArrayList<>();
|
||||
|
||||
// 获取日期列表
|
||||
LocalDate endDate = LocalDate.now();
|
||||
LocalDate startDate = endDate.minusDays(days - 1);
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd");
|
||||
|
||||
for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
|
||||
dateList.add(date.format(formatter));
|
||||
|
||||
// 计算当天注册用户数
|
||||
LocalDateTime start = LocalDateTime.of(date, LocalTime.MIN);
|
||||
LocalDateTime end = LocalDateTime.of(date, LocalTime.MAX);
|
||||
long count = userRepository.countByCreateTimeBetween(start, end);
|
||||
countList.add(count);
|
||||
}
|
||||
|
||||
stats.put("dates", dateList);
|
||||
stats.put("counts", countList);
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getMusicTypeDistribution() {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
List<String> nameList = new ArrayList<>();
|
||||
List<Long> countList = new ArrayList<>();
|
||||
|
||||
// 获取所有音乐类型
|
||||
List<MusicType> types = musicTypeRepository.findAll();
|
||||
for (MusicType type : types) {
|
||||
nameList.add(type.getName());
|
||||
long count = musicRepository.countByTypesContaining(type);
|
||||
countList.add(count);
|
||||
}
|
||||
|
||||
stats.put("names", nameList);
|
||||
stats.put("counts", countList);
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getPlayCountStats(Integer days) {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
List<String> dateList = new ArrayList<>();
|
||||
List<Long> countList = new ArrayList<>();
|
||||
|
||||
// 获取日期列表
|
||||
LocalDate endDate = LocalDate.now();
|
||||
LocalDate startDate = endDate.minusDays(days - 1);
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd");
|
||||
|
||||
for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
|
||||
dateList.add(date.format(formatter));
|
||||
|
||||
// 计算当天播放次数
|
||||
LocalDateTime start = LocalDateTime.of(date, LocalTime.MIN);
|
||||
LocalDateTime end = LocalDateTime.of(date, LocalTime.MAX);
|
||||
long count = playHistoryRepository.countByPlayTimeBetween(start, end);
|
||||
countList.add(count);
|
||||
}
|
||||
|
||||
stats.put("dates", dateList);
|
||||
stats.put("counts", countList);
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getLatestUsers(Integer limit) {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
|
||||
// 获取最新用户
|
||||
List<User> users = userRepository.findAll(
|
||||
PageRequest.of(0, limit, Sort.by(Sort.Direction.DESC, "createTime"))
|
||||
).getContent();
|
||||
|
||||
List<UserDTO> userDTOList = users.stream().map(user -> {
|
||||
UserDTO userDTO = new UserDTO();
|
||||
BeanUtils.copyProperties(user, userDTO);
|
||||
return userDTO;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
stats.put("users", userDTOList);
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getLatestMusics(Integer limit) {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
|
||||
// 获取最新音乐
|
||||
List<Music> musics = musicRepository.findAll(
|
||||
PageRequest.of(0, limit, Sort.by(Sort.Direction.DESC, "createTime"))
|
||||
).getContent();
|
||||
|
||||
List<MusicDTO> musicDTOList = musics.stream().map(music -> {
|
||||
MusicDTO musicDTO = new MusicDTO();
|
||||
BeanUtils.copyProperties(music, musicDTO);
|
||||
return musicDTO;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
stats.put("musics", musicDTOList);
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
package com.test.musichouduan.service.impl;
|
||||
|
||||
import com.test.musichouduan.dto.MusicTypeDTO;
|
||||
import com.test.musichouduan.dto.PageResult;
|
||||
import com.test.musichouduan.dto.PlaylistTypeDTO;
|
||||
import com.test.musichouduan.dto.SingerTypeDTO;
|
||||
import com.test.musichouduan.entity.MusicType;
|
||||
import com.test.musichouduan.entity.PlaylistType;
|
||||
import com.test.musichouduan.entity.SingerType;
|
||||
import com.test.musichouduan.exception.BusinessException;
|
||||
import com.test.musichouduan.repository.MusicRepository;
|
||||
import com.test.musichouduan.repository.MusicTypeRepository;
|
||||
import com.test.musichouduan.repository.PlaylistRepository;
|
||||
import com.test.musichouduan.repository.PlaylistTypeRepository;
|
||||
import com.test.musichouduan.repository.SingerRepository;
|
||||
import com.test.musichouduan.repository.SingerTypeRepository;
|
||||
import com.test.musichouduan.service.TypeService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 类型服务实现类
|
||||
*/
|
||||
@Service
|
||||
public class TypeServiceImpl implements TypeService {
|
||||
|
||||
@Autowired
|
||||
private MusicTypeRepository musicTypeRepository;
|
||||
|
||||
@Autowired
|
||||
private SingerTypeRepository singerTypeRepository;
|
||||
|
||||
@Autowired
|
||||
private PlaylistTypeRepository playlistTypeRepository;
|
||||
|
||||
@Autowired
|
||||
private MusicRepository musicRepository;
|
||||
|
||||
@Autowired
|
||||
private SingerRepository singerRepository;
|
||||
|
||||
@Autowired
|
||||
private PlaylistRepository playlistRepository;
|
||||
|
||||
@Override
|
||||
public List<MusicTypeDTO> getMusicTypeList() {
|
||||
List<MusicType> types = musicTypeRepository.findAll(Sort.by(Sort.Direction.ASC, "id"));
|
||||
return types.stream()
|
||||
.map(this::convertToMusicTypeDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<MusicTypeDTO> getMusicTypePage(Integer page, Integer size, String keyword) {
|
||||
// 构建查询条件
|
||||
Specification<MusicType> spec = (root, query, cb) -> {
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
|
||||
// 关键字查询
|
||||
if (StringUtils.hasText(keyword)) {
|
||||
predicates.add(cb.like(root.get("name"), "%" + keyword + "%"));
|
||||
}
|
||||
|
||||
return cb.and(predicates.toArray(new Predicate[0]));
|
||||
};
|
||||
|
||||
// 执行查询
|
||||
Page<MusicType> typePage = musicTypeRepository.findAll(
|
||||
spec,
|
||||
PageRequest.of(page - 1, size, Sort.by(Sort.Direction.ASC, "id"))
|
||||
);
|
||||
|
||||
// 转换为DTO
|
||||
List<MusicTypeDTO> typeDTOs = typePage.getContent().stream()
|
||||
.map(this::convertToMusicTypeDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new PageResult<>(
|
||||
typeDTOs,
|
||||
typePage.getTotalElements(),
|
||||
page,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public MusicTypeDTO addMusicType(MusicTypeDTO musicTypeDTO) {
|
||||
// 检查名称是否已存在
|
||||
if (musicTypeRepository.existsByName(musicTypeDTO.getName())) {
|
||||
throw new BusinessException("音乐类型名称已存在");
|
||||
}
|
||||
|
||||
// 创建音乐类型实体
|
||||
MusicType musicType = new MusicType();
|
||||
musicType.setName(musicTypeDTO.getName());
|
||||
musicType.setDescription(musicTypeDTO.getDescription());
|
||||
|
||||
// 保存音乐类型
|
||||
MusicType savedMusicType = musicTypeRepository.save(musicType);
|
||||
|
||||
return convertToMusicTypeDTO(savedMusicType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public MusicTypeDTO updateMusicType(Long id, MusicTypeDTO musicTypeDTO) {
|
||||
// 获取音乐类型
|
||||
MusicType musicType = musicTypeRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("音乐类型不存在"));
|
||||
|
||||
// 检查名称是否已存在
|
||||
if (!musicType.getName().equals(musicTypeDTO.getName()) &&
|
||||
musicTypeRepository.existsByName(musicTypeDTO.getName())) {
|
||||
throw new BusinessException("音乐类型名称已存在");
|
||||
}
|
||||
|
||||
// 更新音乐类型信息
|
||||
musicType.setName(musicTypeDTO.getName());
|
||||
musicType.setDescription(musicTypeDTO.getDescription());
|
||||
|
||||
// 保存音乐类型
|
||||
MusicType updatedMusicType = musicTypeRepository.save(musicType);
|
||||
|
||||
return convertToMusicTypeDTO(updatedMusicType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean deleteMusicType(Long id) {
|
||||
// 获取音乐类型
|
||||
MusicType musicType = musicTypeRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("音乐类型不存在"));
|
||||
|
||||
// 检查是否有音乐使用该类型
|
||||
if (musicRepository.existsByTypesContaining(musicType)) {
|
||||
throw new BusinessException("该音乐类型下有音乐,无法删除");
|
||||
}
|
||||
|
||||
// 删除音乐类型
|
||||
musicTypeRepository.delete(musicType);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SingerTypeDTO> getSingerTypeList() {
|
||||
List<SingerType> types = singerTypeRepository.findAll(Sort.by(Sort.Direction.ASC, "id"));
|
||||
return types.stream()
|
||||
.map(this::convertToSingerTypeDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<SingerTypeDTO> getSingerTypePage(Integer page, Integer size, String keyword) {
|
||||
// 构建查询条件
|
||||
Specification<SingerType> spec = (root, query, cb) -> {
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
|
||||
// 关键字查询
|
||||
if (StringUtils.hasText(keyword)) {
|
||||
predicates.add(cb.like(root.get("name"), "%" + keyword + "%"));
|
||||
}
|
||||
|
||||
return cb.and(predicates.toArray(new Predicate[0]));
|
||||
};
|
||||
|
||||
// 执行查询
|
||||
Page<SingerType> typePage = singerTypeRepository.findAll(
|
||||
spec,
|
||||
PageRequest.of(page - 1, size, Sort.by(Sort.Direction.ASC, "id"))
|
||||
);
|
||||
|
||||
// 转换为DTO
|
||||
List<SingerTypeDTO> typeDTOs = typePage.getContent().stream()
|
||||
.map(this::convertToSingerTypeDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new PageResult<>(
|
||||
typeDTOs,
|
||||
typePage.getTotalElements(),
|
||||
page,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public SingerTypeDTO addSingerType(SingerTypeDTO singerTypeDTO) {
|
||||
// 检查名称是否已存在
|
||||
if (singerTypeRepository.existsByName(singerTypeDTO.getName())) {
|
||||
throw new BusinessException("歌手类型名称已存在");
|
||||
}
|
||||
|
||||
// 创建歌手类型实体
|
||||
SingerType singerType = new SingerType();
|
||||
singerType.setName(singerTypeDTO.getName());
|
||||
singerType.setDescription(singerTypeDTO.getDescription());
|
||||
|
||||
// 保存歌手类型
|
||||
SingerType savedSingerType = singerTypeRepository.save(singerType);
|
||||
|
||||
return convertToSingerTypeDTO(savedSingerType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public SingerTypeDTO updateSingerType(Long id, SingerTypeDTO singerTypeDTO) {
|
||||
// 获取歌手类型
|
||||
SingerType singerType = singerTypeRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("歌手类型不存在"));
|
||||
|
||||
// 检查名称是否已存在
|
||||
if (!singerType.getName().equals(singerTypeDTO.getName()) &&
|
||||
singerTypeRepository.existsByName(singerTypeDTO.getName())) {
|
||||
throw new BusinessException("歌手类型名称已存在");
|
||||
}
|
||||
|
||||
// 更新歌手类型信息
|
||||
singerType.setName(singerTypeDTO.getName());
|
||||
singerType.setDescription(singerTypeDTO.getDescription());
|
||||
|
||||
// 保存歌手类型
|
||||
SingerType updatedSingerType = singerTypeRepository.save(singerType);
|
||||
|
||||
return convertToSingerTypeDTO(updatedSingerType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean deleteSingerType(Long id) {
|
||||
// 获取歌手类型
|
||||
SingerType singerType = singerTypeRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("歌手类型不存在"));
|
||||
|
||||
// 检查是否有歌手使用该类型
|
||||
if (singerRepository.existsByType(singerType)) {
|
||||
throw new BusinessException("该歌手类型下有歌手,无法删除");
|
||||
}
|
||||
|
||||
// 删除歌手类型
|
||||
singerTypeRepository.delete(singerType);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PlaylistTypeDTO> getPlaylistTypeList() {
|
||||
List<PlaylistType> types = playlistTypeRepository.findAll(Sort.by(Sort.Direction.ASC, "id"));
|
||||
return types.stream()
|
||||
.map(this::convertToPlaylistTypeDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<PlaylistTypeDTO> getPlaylistTypePage(Integer page, Integer size, String keyword) {
|
||||
// 构建查询条件
|
||||
Specification<PlaylistType> spec = (root, query, cb) -> {
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
|
||||
// 关键字查询
|
||||
if (StringUtils.hasText(keyword)) {
|
||||
predicates.add(cb.like(root.get("name"), "%" + keyword + "%"));
|
||||
}
|
||||
|
||||
return cb.and(predicates.toArray(new Predicate[0]));
|
||||
};
|
||||
|
||||
// 执行查询
|
||||
Page<PlaylistType> typePage = playlistTypeRepository.findAll(
|
||||
spec,
|
||||
PageRequest.of(page - 1, size, Sort.by(Sort.Direction.ASC, "id"))
|
||||
);
|
||||
|
||||
// 转换为DTO
|
||||
List<PlaylistTypeDTO> typeDTOs = typePage.getContent().stream()
|
||||
.map(this::convertToPlaylistTypeDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new PageResult<>(
|
||||
typeDTOs,
|
||||
typePage.getTotalElements(),
|
||||
page,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public PlaylistTypeDTO addPlaylistType(PlaylistTypeDTO playlistTypeDTO) {
|
||||
// 检查名称是否已存在
|
||||
if (playlistTypeRepository.existsByName(playlistTypeDTO.getName())) {
|
||||
throw new BusinessException("歌单类型名称已存在");
|
||||
}
|
||||
|
||||
// 创建歌单类型实体
|
||||
PlaylistType playlistType = new PlaylistType();
|
||||
playlistType.setName(playlistTypeDTO.getName());
|
||||
playlistType.setDescription(playlistTypeDTO.getDescription());
|
||||
|
||||
// 保存歌单类型
|
||||
PlaylistType savedPlaylistType = playlistTypeRepository.save(playlistType);
|
||||
|
||||
return convertToPlaylistTypeDTO(savedPlaylistType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public PlaylistTypeDTO updatePlaylistType(Long id, PlaylistTypeDTO playlistTypeDTO) {
|
||||
// 获取歌单类型
|
||||
PlaylistType playlistType = playlistTypeRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("歌单类型不存在"));
|
||||
|
||||
// 检查名称是否已存在
|
||||
if (!playlistType.getName().equals(playlistTypeDTO.getName()) &&
|
||||
playlistTypeRepository.existsByName(playlistTypeDTO.getName())) {
|
||||
throw new BusinessException("歌单类型名称已存在");
|
||||
}
|
||||
|
||||
// 更新歌单类型信息
|
||||
playlistType.setName(playlistTypeDTO.getName());
|
||||
playlistType.setDescription(playlistTypeDTO.getDescription());
|
||||
|
||||
// 保存歌单类型
|
||||
PlaylistType updatedPlaylistType = playlistTypeRepository.save(playlistType);
|
||||
|
||||
return convertToPlaylistTypeDTO(updatedPlaylistType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean deletePlaylistType(Long id) {
|
||||
// 获取歌单类型
|
||||
PlaylistType playlistType = playlistTypeRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException("歌单类型不存在"));
|
||||
|
||||
// 检查是否有歌单使用该类型
|
||||
if (playlistRepository.existsByType(playlistType)) {
|
||||
throw new BusinessException("该歌单类型下有歌单,无法删除");
|
||||
}
|
||||
|
||||
// 删除歌单类型
|
||||
playlistTypeRepository.delete(playlistType);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将音乐类型实体转换为DTO
|
||||
*
|
||||
* @param musicType 音乐类型实体
|
||||
* @return 音乐类型DTO
|
||||
*/
|
||||
private MusicTypeDTO convertToMusicTypeDTO(MusicType musicType) {
|
||||
MusicTypeDTO musicTypeDTO = new MusicTypeDTO();
|
||||
BeanUtils.copyProperties(musicType, musicTypeDTO);
|
||||
return musicTypeDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将歌手类型实体转换为DTO
|
||||
*
|
||||
* @param singerType 歌手类型实体
|
||||
* @return 歌手类型DTO
|
||||
*/
|
||||
private SingerTypeDTO convertToSingerTypeDTO(SingerType singerType) {
|
||||
SingerTypeDTO singerTypeDTO = new SingerTypeDTO();
|
||||
BeanUtils.copyProperties(singerType, singerTypeDTO);
|
||||
return singerTypeDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将歌单类型实体转换为DTO
|
||||
*
|
||||
* @param playlistType 歌单类型实体
|
||||
* @return 歌单类型DTO
|
||||
*/
|
||||
private PlaylistTypeDTO convertToPlaylistTypeDTO(PlaylistType playlistType) {
|
||||
PlaylistTypeDTO playlistTypeDTO = new PlaylistTypeDTO();
|
||||
BeanUtils.copyProperties(playlistType, playlistTypeDTO);
|
||||
return playlistTypeDTO;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
package com.test.musichouduan.service.impl;
|
||||
|
||||
import cn.dev33.satoken.secure.BCrypt;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.test.musichouduan.dto.*;
|
||||
import com.test.musichouduan.entity.User;
|
||||
import com.test.musichouduan.repository.*;
|
||||
import com.test.musichouduan.service.UserService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 用户服务实现类
|
||||
*/
|
||||
@Service
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
private UserCollectMusicRepository userCollectMusicRepository;
|
||||
|
||||
@Autowired
|
||||
private UserCollectPlaylistRepository userCollectPlaylistRepository;
|
||||
|
||||
@Autowired
|
||||
private UserCollectSingerRepository userCollectSingerRepository;
|
||||
|
||||
@Autowired
|
||||
private PlaylistRepository playlistRepository;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public UserDTO register(RegisterRequest request) {
|
||||
// 检查用户名是否已存在
|
||||
if (userRepository.existsByUsername(request.getUsername())) {
|
||||
throw new RuntimeException("用户名已存在");
|
||||
}
|
||||
|
||||
// 检查邮箱是否已存在
|
||||
if (userRepository.existsByEmail(request.getEmail())) {
|
||||
throw new RuntimeException("邮箱已存在");
|
||||
}
|
||||
|
||||
// 检查两次密码是否一致
|
||||
if (!request.getPassword().equals(request.getConfirmPassword())) {
|
||||
throw new RuntimeException("两次密码不一致");
|
||||
}
|
||||
|
||||
// 创建用户
|
||||
User user = new User();
|
||||
user.setUsername(request.getUsername());
|
||||
user.setPassword(BCrypt.hashpw(request.getPassword()));
|
||||
user.setEmail(request.getEmail());
|
||||
user.setNickname(request.getNickname() != null ? request.getNickname() : request.getUsername());
|
||||
user.setGender(0);
|
||||
user.setRole(0);
|
||||
user.setStatus(1);
|
||||
|
||||
// 保存用户
|
||||
user = userRepository.save(user);
|
||||
|
||||
// 返回用户DTO
|
||||
return convertToDTO(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginResultDTO login(LoginRequest request) {
|
||||
// 根据用户名查找用户
|
||||
User user = userRepository.findByUsername(request.getUsername())
|
||||
.orElseThrow(() -> new RuntimeException("用户名或密码错误"));
|
||||
|
||||
// 检查用户状态
|
||||
if (user.getStatus() == 0) {
|
||||
throw new RuntimeException("账号已被禁用");
|
||||
}
|
||||
|
||||
// 检查密码
|
||||
if (!BCrypt.checkpw(request.getPassword(), user.getPassword())) {
|
||||
throw new RuntimeException("用户名或密码错误");
|
||||
}
|
||||
|
||||
// 登录
|
||||
StpUtil.login(user.getId());
|
||||
|
||||
// 设置角色
|
||||
if (user.getRole() == 1) {
|
||||
// 打印调试信息
|
||||
System.out.println("设置用户ID: " + user.getId() + " 为admin角色");
|
||||
}
|
||||
|
||||
// 获取令牌信息
|
||||
String token = StpUtil.getTokenValue();
|
||||
String tokenName = StpUtil.getTokenName();
|
||||
|
||||
// 创建登录结果DTO
|
||||
LoginResultDTO resultDTO = new LoginResultDTO();
|
||||
resultDTO.setUser(convertToDTO(user));
|
||||
resultDTO.setToken(token);
|
||||
resultDTO.setTokenName(tokenName);
|
||||
resultDTO.setTokenPrefix("");
|
||||
|
||||
// 返回登录结果DTO
|
||||
return resultDTO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDTO getCurrentUser() {
|
||||
// 获取当前登录用户ID
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
|
||||
// 根据ID查找用户
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new RuntimeException("用户不存在"));
|
||||
|
||||
// 返回用户DTO
|
||||
return getUserStats(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDTO getUserById(Long id) {
|
||||
// 根据ID查找用户
|
||||
User user = userRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("用户不存在"));
|
||||
|
||||
// 返回用户DTO
|
||||
return convertToDTO(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDTO getUserByUsername(String username) {
|
||||
// 根据用户名查找用户
|
||||
User user = userRepository.findByUsername(username)
|
||||
.orElseThrow(() -> new RuntimeException("用户不存在"));
|
||||
|
||||
// 返回用户DTO
|
||||
return convertToDTO(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public UserDTO updateUser(UserUpdateRequest request) {
|
||||
// 获取当前登录用户ID
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
|
||||
// 根据ID查找用户
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new RuntimeException("用户不存在"));
|
||||
|
||||
// 更新用户信息
|
||||
if (request.getNickname() != null) {
|
||||
user.setNickname(request.getNickname());
|
||||
}
|
||||
if (request.getAvatar() != null) {
|
||||
user.setAvatar(request.getAvatar());
|
||||
}
|
||||
if (request.getGender() != null) {
|
||||
user.setGender(request.getGender());
|
||||
}
|
||||
if (request.getEmail() != null) {
|
||||
user.setEmail(request.getEmail());
|
||||
}
|
||||
if (request.getPhone() != null) {
|
||||
user.setPhone(request.getPhone());
|
||||
}
|
||||
if (request.getIntroduction() != null) {
|
||||
user.setIntroduction(request.getIntroduction());
|
||||
}
|
||||
|
||||
// 保存用户
|
||||
user = userRepository.save(user);
|
||||
|
||||
// 返回用户DTO
|
||||
return convertToDTO(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean updatePassword(PasswordUpdateRequest request) {
|
||||
// 获取当前登录用户ID
|
||||
Long userId = StpUtil.getLoginIdAsLong();
|
||||
|
||||
// 根据ID查找用户
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new RuntimeException("用户不存在"));
|
||||
|
||||
// 检查旧密码
|
||||
if (!BCrypt.checkpw(request.getOldPassword(), user.getPassword())) {
|
||||
throw new RuntimeException("旧密码错误");
|
||||
}
|
||||
|
||||
// 检查两次密码是否一致
|
||||
if (!request.getNewPassword().equals(request.getConfirmPassword())) {
|
||||
throw new RuntimeException("两次密码不一致");
|
||||
}
|
||||
|
||||
// 更新密码
|
||||
user.setPassword(BCrypt.hashpw(request.getNewPassword()));
|
||||
|
||||
// 保存用户
|
||||
userRepository.save(user);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDTO getUserStats(Long userId) {
|
||||
// 根据ID查找用户
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new RuntimeException("用户不存在"));
|
||||
|
||||
// 转换为DTO
|
||||
UserDTO userDTO = convertToDTO(user);
|
||||
|
||||
// 获取统计数据
|
||||
userDTO.setCollectMusicCount(userCollectMusicRepository.countByUser(user));
|
||||
userDTO.setCollectPlaylistCount(userCollectPlaylistRepository.countByUser(user));
|
||||
userDTO.setCollectSingerCount(userCollectSingerRepository.countByUser(user));
|
||||
userDTO.setCreatePlaylistCount(playlistRepository.countByCreator(user));
|
||||
|
||||
return userDTO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<UserDTO> getUserList(Integer page, Integer size, String keyword) {
|
||||
// 创建分页对象
|
||||
Pageable pageable = PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createTime"));
|
||||
|
||||
// 创建查询条件
|
||||
Specification<User> specification = (root, query, criteriaBuilder) -> {
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
|
||||
// 关键字查询
|
||||
if (keyword != null && !keyword.isEmpty()) {
|
||||
predicates.add(criteriaBuilder.or(
|
||||
criteriaBuilder.like(root.get("username"), "%" + keyword + "%"),
|
||||
criteriaBuilder.like(root.get("nickname"), "%" + keyword + "%"),
|
||||
criteriaBuilder.like(root.get("email"), "%" + keyword + "%")
|
||||
));
|
||||
}
|
||||
|
||||
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
|
||||
};
|
||||
|
||||
// 查询
|
||||
Page<User> userPage = userRepository.findAll(specification, pageable);
|
||||
|
||||
// 转换为DTO
|
||||
List<UserDTO> userDTOList = userPage.getContent().stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 返回分页结果
|
||||
return new PageResult<>(userDTOList, userPage.getTotalElements(), page, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean deleteUser(Long id) {
|
||||
// 检查用户是否存在
|
||||
if (!userRepository.existsById(id)) {
|
||||
throw new RuntimeException("用户不存在");
|
||||
}
|
||||
|
||||
// 删除用户
|
||||
userRepository.deleteById(id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean updateUserStatus(Long id, Integer status) {
|
||||
// 根据ID查找用户
|
||||
User user = userRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("用户不存在"));
|
||||
|
||||
// 更新状态
|
||||
user.setStatus(status);
|
||||
|
||||
// 保存用户
|
||||
userRepository.save(user);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDTO adminUpdateUser(Long id, UserUpdateRequest request) {
|
||||
User user = getById(id);
|
||||
|
||||
// 更新用户信息
|
||||
if (request.getNickname() != null) {
|
||||
user.setNickname(request.getNickname());
|
||||
}
|
||||
if (request.getAvatar() != null) {
|
||||
user.setAvatar(request.getAvatar());
|
||||
}
|
||||
if (request.getGender() != null) {
|
||||
user.setGender(request.getGender());
|
||||
}
|
||||
if (request.getEmail() != null) {
|
||||
user.setEmail(request.getEmail());
|
||||
}
|
||||
if (request.getPhone() != null) {
|
||||
user.setPhone(request.getPhone());
|
||||
}
|
||||
if (request.getIntroduction() != null) {
|
||||
user.setIntroduction(request.getIntroduction());
|
||||
}
|
||||
|
||||
// 保存用户
|
||||
User savedUser = userRepository.save(user);
|
||||
|
||||
return convertToDTO(savedUser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getById(Long id) {
|
||||
return userRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("用户不存在"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将用户实体转换为DTO
|
||||
*
|
||||
* @param user 用户实体
|
||||
* @return 用户DTO
|
||||
*/
|
||||
private UserDTO convertToDTO(User user) {
|
||||
UserDTO userDTO = new UserDTO();
|
||||
BeanUtils.copyProperties(user, userDTO);
|
||||
|
||||
// 确保头像URL是完整路径
|
||||
if (userDTO.getAvatar() != null && !userDTO.getAvatar().startsWith("http")) {
|
||||
userDTO.setAvatar("http://localhost:8085" + userDTO.getAvatar());
|
||||
}
|
||||
|
||||
return userDTO;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user