feat: 初始化音乐项目后台管理系统- 新增后台管理相关页面和功能组件
- 实现管理员评论管理、数据统计等功能 - 添加后台布局和路由管理 -配置数据库和Redis连接 - 实现文件上传和访问路径配置 - 添加Sa-Token安全配置
This commit is contained in:
79
.gitignore
vendored
Normal file
79
.gitignore
vendored
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
### Example user template template
|
||||||
|
### Example user template
|
||||||
|
|
||||||
|
# IntelliJ project files
|
||||||
|
.log
|
||||||
|
*.gz
|
||||||
|
.gz
|
||||||
|
*/.gz
|
||||||
|
*/.log
|
||||||
|
target
|
||||||
|
*.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文件
|
||||||
|
|
||||||
|
#忽略所有.class文件
|
||||||
|
*.class
|
||||||
|
#忽略所有.log文件
|
||||||
|
|
||||||
|
#忽略所有.tmp文件
|
||||||
|
*.tmp
|
||||||
|
#忽略所有.swp文件
|
||||||
|
*.swp
|
||||||
|
node_modules/
|
||||||
|
/dist/
|
||||||
|
|
||||||
|
.git
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
dist/
|
||||||
|
npm-debug.log
|
||||||
|
.vscode/
|
||||||
219
README.md
Normal file
219
README.md
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
# 音乐播放应用系统
|
||||||
|
|
||||||
|
## 项目概述
|
||||||
|
|
||||||
|
这是一个功能完整的音乐播放应用系统,包含前后端两部分。系统支持用户注册登录、音乐播放、歌单管理、歌手管理、评论互动等功能,为用户提供全面的音乐体验。
|
||||||
|
|
||||||
|
### 系统架构
|
||||||
|
|
||||||
|
本项目采用前后端分离架构:
|
||||||
|
- 前端:基于Vue 3的单页面应用
|
||||||
|
- 后端:基于Spring Boot的RESTful API服务
|
||||||
|
- 数据库:MySQL关系型数据库
|
||||||
|
- 缓存:Redis缓存服务
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
|
||||||
|
### 后端技术栈
|
||||||
|
|
||||||
|
- **核心框架**:Spring Boot 3.x
|
||||||
|
- **ORM框架**:Spring Data JPA + Hibernate
|
||||||
|
- **安全认证**:Sa-Token
|
||||||
|
- **数据库**:MySQL 8.x
|
||||||
|
- **缓存**:Redis
|
||||||
|
- **API文档**:Knife4j/Swagger
|
||||||
|
- **文件处理**:自定义文件上传服务
|
||||||
|
|
||||||
|
### 前端技术栈
|
||||||
|
|
||||||
|
- **核心框架**:Vue 3 (组合式API)
|
||||||
|
- **构建工具**:Vite
|
||||||
|
- **状态管理**:Pinia
|
||||||
|
- **UI组件**:Element Plus
|
||||||
|
- **路由管理**:Vue Router
|
||||||
|
- **HTTP请求**:Axios
|
||||||
|
- **音频处理**:Web Audio API
|
||||||
|
|
||||||
|
## 功能模块
|
||||||
|
|
||||||
|
### 1. 用户系统
|
||||||
|
|
||||||
|
- 用户注册与登录
|
||||||
|
- 个人信息管理
|
||||||
|
- 收藏管理(音乐、歌单、歌手)
|
||||||
|
- 播放历史记录
|
||||||
|
- 权限控制(普通用户/管理员)
|
||||||
|
|
||||||
|
### 2. 音乐播放系统
|
||||||
|
|
||||||
|
- 音乐播放、暂停、上一首、下一首
|
||||||
|
- 播放列表管理
|
||||||
|
- 播放模式(顺序播放、单曲循环、列表循环)
|
||||||
|
- 歌词同步显示
|
||||||
|
- 进度控制与音量调节
|
||||||
|
- 播放统计
|
||||||
|
|
||||||
|
### 3. 音乐管理系统
|
||||||
|
|
||||||
|
- 音乐上传与编辑
|
||||||
|
- 音乐分类管理
|
||||||
|
- 音乐搜索
|
||||||
|
- 热门音乐推荐
|
||||||
|
- 最新音乐展示
|
||||||
|
|
||||||
|
### 4. 歌手管理系统
|
||||||
|
|
||||||
|
- 歌手信息管理
|
||||||
|
- 歌手分类
|
||||||
|
- 歌手音乐关联
|
||||||
|
- 歌手搜索
|
||||||
|
|
||||||
|
### 5. 歌单管理系统
|
||||||
|
|
||||||
|
- 歌单创建与编辑
|
||||||
|
- 歌单分类
|
||||||
|
- 歌单音乐管理
|
||||||
|
- 歌单收藏
|
||||||
|
- 官方推荐歌单
|
||||||
|
|
||||||
|
### 6. 评论系统
|
||||||
|
|
||||||
|
- 发表评论
|
||||||
|
- 回复评论
|
||||||
|
- 评论管理
|
||||||
|
|
||||||
|
### 7. 首页推荐系统
|
||||||
|
|
||||||
|
- 轮播图展示
|
||||||
|
- 热门音乐推荐
|
||||||
|
- 最新音乐展示
|
||||||
|
- 推荐歌单
|
||||||
|
- 热门歌手
|
||||||
|
|
||||||
|
## 数据模型
|
||||||
|
|
||||||
|
### 核心实体
|
||||||
|
|
||||||
|
1. **用户(User)**:用户基本信息
|
||||||
|
2. **音乐(Music)**:音乐信息、文件路径、歌词
|
||||||
|
3. **歌手(Singer)**:歌手信息
|
||||||
|
4. **歌单(Playlist)**:歌单信息
|
||||||
|
5. **评论(Comment)**:评论内容
|
||||||
|
6. **类型(Type)**:音乐类型、歌手类型、歌单类型
|
||||||
|
7. **轮播图(Banner)**:首页轮播图
|
||||||
|
|
||||||
|
### 核心关系
|
||||||
|
|
||||||
|
1. **用户-音乐**:收藏关系、播放历史
|
||||||
|
2. **用户-歌单**:创建关系、收藏关系
|
||||||
|
3. **用户-歌手**:收藏关系
|
||||||
|
4. **音乐-歌手**:多对多关系
|
||||||
|
5. **音乐-类型**:多对多关系
|
||||||
|
6. **歌单-音乐**:多对多关系
|
||||||
|
7. **歌手-类型**:多对一关系
|
||||||
|
8. **歌单-类型**:多对一关系
|
||||||
|
|
||||||
|
## 部署指南
|
||||||
|
|
||||||
|
### 环境要求
|
||||||
|
|
||||||
|
- JDK 17+
|
||||||
|
- Node.js 16+
|
||||||
|
- MySQL 8.x
|
||||||
|
- Redis 6.x
|
||||||
|
|
||||||
|
### 后端部署
|
||||||
|
|
||||||
|
1. 配置数据库
|
||||||
|
```sql
|
||||||
|
CREATE DATABASE music_db CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 修改配置文件
|
||||||
|
- 编辑 `music-houduan/src/main/resources/application.yml`
|
||||||
|
- 配置数据库连接信息
|
||||||
|
- 配置Redis连接信息
|
||||||
|
- 配置文件上传路径
|
||||||
|
|
||||||
|
3. 构建项目
|
||||||
|
```bash
|
||||||
|
cd music-houduan
|
||||||
|
mvn clean package -DskipTests
|
||||||
|
```
|
||||||
|
|
||||||
|
4. 运行项目
|
||||||
|
```bash
|
||||||
|
java -jar target/music-houduan-1.0.0.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
### 前端部署
|
||||||
|
|
||||||
|
1. 安装依赖
|
||||||
|
```bash
|
||||||
|
cd music-qianduan
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 修改配置
|
||||||
|
- 编辑 `.env` 文件,配置API地址
|
||||||
|
|
||||||
|
3. 构建项目
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
4. 部署静态资源
|
||||||
|
- 将 `dist` 目录下的文件部署到Web服务器
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
### 管理员账号
|
||||||
|
|
||||||
|
- 用户名:admin
|
||||||
|
- 密码:123456
|
||||||
|
|
||||||
|
### 普通用户
|
||||||
|
|
||||||
|
- 可以自行注册或使用以下测试账号:
|
||||||
|
- 用户名:test
|
||||||
|
- 密码:123456
|
||||||
|
|
||||||
|
### 基本操作流程
|
||||||
|
|
||||||
|
1. **音乐播放**
|
||||||
|
- 在首页或音乐列表页选择音乐
|
||||||
|
- 点击播放按钮开始播放
|
||||||
|
- 使用播放器控制播放进度、音量等
|
||||||
|
|
||||||
|
2. **歌单管理**
|
||||||
|
- 登录后可创建个人歌单
|
||||||
|
- 添加音乐到歌单
|
||||||
|
- 收藏其他用户的歌单
|
||||||
|
|
||||||
|
3. **音乐上传(管理员)**
|
||||||
|
- 登录管理员账号
|
||||||
|
- 进入后台管理
|
||||||
|
- 上传音乐文件和封面
|
||||||
|
|
||||||
|
4. **评论互动**
|
||||||
|
- 在音乐、歌单、歌手详情页发表评论
|
||||||
|
- 回复其他用户的评论
|
||||||
|
|
||||||
|
## 系统截图
|
||||||
|
|
||||||
|
(此处可添加系统主要页面的截图)
|
||||||
|
|
||||||
|
## 开发团队
|
||||||
|
|
||||||
|
- 前端开发:XXX
|
||||||
|
- 后端开发:XXX
|
||||||
|
- 产品设计:XXX
|
||||||
|
- 测试:XXX
|
||||||
|
|
||||||
|
## 版权声明
|
||||||
|
|
||||||
|
本项目仅用于学习和演示目的,不得用于商业用途。音乐文件版权归原作者所有。
|
||||||
|
|
||||||
|
## 联系方式
|
||||||
|
|
||||||
|
如有问题或建议,请联系:example@example.com
|
||||||
2
doc/音乐下载.txt
Normal file
2
doc/音乐下载.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
https://www.gequbao.com/
|
||||||
|
https://www.gequhai.com/
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user