目录

一、项目截图

二、创建 SpringBoot 项目

1、在 IDEA 中创建一个 SpringBoot 项目

2、设置项目名称

3、选择项目依赖

4、选择项目存放路径,就可以创建出一个 SpringBoot 项目

三、配置数据库和xml

1、打开application.properties

2、配置如下信息

四、数据库设计

五、工具包

1、ResponseBodyMessage 类

2、Constant 类

3、数据加密

1. MD5 加密

MD5 的使用

2. BCrypt 加密

Bcrypt 的使用

3. BCrypt加密与MD5加密的区别:

六、配置拦截器

1、创建 config 包,在 config 包中创建 LoginInterceptor 类

2、在 config 包中创建 AppConfig 类

七、实现登录模块

1、登录功能的请求和响应设计

2、创建 User 类

3、创建对应的 Mapper 和 Controller

1. 创建接口 UserMapper

2. 创建 UserMapper.xml

4、在 UserMapper 接口中新增 selectByName 方法

5、创建 UserController 类

6、登录成功测试

7、前端代码

八、实现注册模块

1、注册功能的请求和响应设计

2、在 UserMapper 接口新增方法

3、UserMapper.xml 文件中添加代码

4、在 UserController 类中添加 register 方法

5、注册功能测试

6、前端代码

九、实现上传音乐模块

1、上传音乐功能的请求和响应设计

2、创建 Music 类

3、创建接口 MusicMapper

4、创建 MusicMapper.xml

5、创建 MusicController 类

6、上传音乐功能测试

7、前端代码

十、实现播放音乐模块

1、播放音乐功能的请求和响应设计

2、在 MusicController 类中添加 playMusic 方法

3、播放音乐功能测试

4、前端代码

十一、实现删除音乐模块

1、删除单个音乐

1. 删除单个音乐的请求和响应设计

2. 在 MusicMapper 接口中添加代码

3. MusicMapper.xml 文件中添加代码

4. 在 MusicController 类中添加 deleteByMusicId 方法

5. 删除单个音乐功能测试

6. 前端代码

2、批量删除选中的音乐

1. 批量删除选中的音乐的请求和响应设计

2. 在 MusicController 类中添加 deleteSelMusic 方法

3. 批量删除选中的音乐功能测试

4. 前端代码

十二、实现查询音乐模块

1、查询音乐的请求和响应设计

2、在 MusicMapper 接口中添加代码

3、MusicMapper.xml 文件中添加代码

4、在 MusicController 类中添加 findMusic 方法

5、查询音乐功能测试

1. 查询所有的音乐

2. 模糊匹配,查询指定的音乐

6、前端代码

十三、实现收藏音乐模块

1、收藏音乐的请求和响应设计

2、创建 LoveMusic 类

3、创建接口 LoveMusicMapper

4、创建 LoveMusicMapper.xml

5、创建 LoveMusicController 类

6、收藏音乐功能测试

7、前端代码

十四、实现查询收藏的音乐模块

1、查询收藏音乐的请求和响应设计

2、在 LoveMusicMapper 接口中添加代码

3、LoveMusicMapper.xml 文件中添加代码

4、在 LoveMusicController 类中添加 findLoveMusic 方法

5、查询收藏音乐功能测试

1. 查询所有的收藏音乐

2. 模糊匹配,查询指定的收藏音乐

6、前端代码

十五、实现取消收藏音乐模块

1、取消(移除)收藏音乐的请求和响应设计

2、在 LoveMusicMapper 接口中添加代码

3、LoveMusicMapper.xml 文件中添加代码

4、在 LoveMusicController 类中添加 removeLoveMusic 方法

5、取消收藏音乐功能测试

6、前端代码

十六、完善删除音乐功能

1、在 LoveMusicMapper 接口中添加代码

2、LoveMusicMapper.xml 文件中添加代码

3、调整 MusicController 类中的 deleteMusicByMusicId 和 deleteSelMusic 方法

4、功能测试

1. 查询已上传的音乐

2. 查询已收藏的音乐

3. 删除已上传的音乐(music 表中的数据)

4. 上传的音乐删除后,收藏的音乐也会被删除


一、项目截图

二、创建 SpringBoot 项目

1、在 IDEA 中创建一个 SpringBoot 项目

2、设置项目名称

3、选择项目依赖

4、选择项目存放路径,就可以创建出一个 SpringBoot 项目

三、配置数据库和xml

1、打开application.properties

2、配置如下信息

#配置数据库
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/onlinemusic?characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username=你的用户名
spring.datasource.password=你的密码
spring.datasource.driver-class-name=com.mysql.jdbc.Driver#配置xml
mybatis.mapper-locations=classpath:mybatis/**Mapper.xml# 音乐上传后的路径
music.local.path=E:/SaveMusic/(填写存放歌曲的路径)#配置springboot上传文件的大小,默认每个文件的配置最大为15Mb,单次请求的文件的总数不能大于100Mb
spring.servlet.multipart.max-file-size = 15MB
spring.servlet.multipart.max-request-size=100MB# 配置springboot日志调试模式是否开启
debug=true# 设置打印日志的级别,及打印sql语句
#日志级别:trace,debug,info,warn,error
#基本日志
logging.level.root=INFO
logging.level.com.example.onlinemusic.mapper=debug
#扫描的包:druid.sql.Statement类和frank包
logging.level.druid.sql.Statement=DEBUG
logging.level.com.example=DEBUG

四、数据库设计

-- 创建数据库
drop database if exists `onlinemusic`;
create database if not exists `onlinemusic` character set utf8;
-- 使用数据库
use `onlinemusic`;-- 用户表
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`userid` INT PRIMARY KEY AUTO_INCREMENT comment '用户id',
`username` varchar(20) NOT NULL comment '用户名',
`password` varchar(255) NOT NULL comment '密码'
);-- 歌曲表
DROP TABLE IF EXISTS `music`;
CREATE TABLE `music` (
`musicid` int PRIMARY KEY AUTO_INCREMENT comment '歌曲id',
`title` varchar(50) NOT NULL comment '歌曲名称',
`singer` varchar(30) NOT NULL comment '歌手',
`time` varchar(13) NOT NULL comment '上传歌曲时间',
`url` varchar(1000) NOT NULL comment '存放歌曲的路径',
`userid` int(11) NOT NULL comment '上传歌曲的用户'
);-- 歌曲收藏表
DROP TABLE IF EXISTS `lovemusic`;
CREATE TABLE `lovemusic` (
`loveid` int PRIMARY KEY AUTO_INCREMENT comment '收藏歌曲的id',
`user_id` int(11) NOT NULL comment '收藏歌曲的用户id',
`music_id` int(11) NOT NULL comment '歌曲id'
);

五、工具包

在 package com.example.musicserver 目录下创建一个 tools 包(工具包),在这个包中存放整个项目要使用的工具类。

1、ResponseBodyMessage 类

  • 设计统一的响应体工具类,因为做任何操作时都需要响应,所以封装一个通用的响应工具类,这个工具类设计成一个泛型类。
package com.example.onlinemusic.tools;import lombok.Data;@Data
public class ResponseBodyMessage <T>{private int status; //状态码private String message; // 返回的信息(出错的原因等)private T data; // 返回给前端的数据(因为返回的数据类型不确定,可能是 String,boolea,int ...,因此使用泛型)public ResponseBodyMessage(int status, String message, T data) {this.status = status;this.message = message;this.data = data;}
}

2、Constant 类

  • 这个类用来存储不变的常量。 例如:设置 session 对象中的 key 值,key 是一个不变的字符串。
  • 如果在其他地方获取对应的 session 就可以通过这个类中的字符串进行获取。
package com.example.onlinemusic.tools;public class Constant {public static final String USER_SESSION_KEY= "USERINFO_SESSION_KEY"; // 设置 session 中的 key 值
}

3、数据加密

1. MD5 加密

MD5是一个安全的散列算法,输入两个不同的明文不会得到相同的输出值,根据输出值,不能得到原始的明文,即其过程不可逆; 但是虽然不可逆,但是不是说就是安全的。因为自从出现彩虹表后,这样的密码也"不安全"。

  • 彩虹表:彩虹表就是一个庞大的、针对各种可能的字母组合预先计算好的哈希值的集合,不一定是针对MD5算法的,各种算法的都有,有了它可以快速的破解各类密码。越是复杂的密码,需要的彩虹表就越大,现在主流的彩虹表都是100G以上。

更安全的做法是加盐或者长密码等做法,让整个加密的字符串变的更长,破解时间变慢。密码学的应用安全,是建立在破解所要付出的成本远超出能得到的利益上的。

  • 加盐的做法:盐是在每个密码中加入一些单词来变成一个新的密码,存入数据库当中。

MD5 的使用

(1)在 pom.xml 文件中添加依赖(添加到 <dependencies> </dependencies> 标签内)

<!-- md5 依赖 -->
<dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId>
</dependency>
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.9</version>
</dependency>

(2)在 tools 包中创建 MD5Util 类

package com.example.onlinemusic.tools;import org.apache.commons.codec.digest.DigestUtils;public class MD5Util {// 定义一个固定的盐值private static final String salt = "1j2a3v4a5"; // 盐值可以自定义public static String md5(String src) {return DigestUtils.md5Hex(src);}/*** 第一次加密 :模拟前端自己加密,然后传到后端** @param inputPass* @return*/public static String inputPassToFormPass(String inputPass) {String str = "" + salt.charAt(1) + salt.charAt(3) + inputPass+ salt.charAt(5) + salt.charAt(6);return md5(str);}/*** 第2次MD5加密** @param formPass 前端加密过的密码,传给后端进行第2次加密* @param salt     用户数据库当中的盐值* @return*/public static String formPassToDBPass(String formPass, String salt) {String str = "" + salt.charAt(0) + salt.charAt(2) + formPass + salt.charAt(5)+ salt.charAt(4);return md5(str);}/*** 上面两个函数合到一起进行调用** @param saltDB* @return* @paraminputPass*/public static String inputPassToDbPass(String inputPass, String saltDB) {String formPass = inputPassToFormPass(inputPass);String dbPass = formPassToDBPass(formPass, saltDB);return dbPass;}public static void main(String[] args) {System.out.println("对用户输入密码进行第1次加密:" + inputPassToFormPass("123456"));System.out.println("对用户输入密码进行第2次加密:" + formPassToDBPass(inputPassToFormPass("123456"), salt));System.out.println("对用户输入密码进行第2次加密:" + inputPassToDbPass("123456", salt));}
}

运行结果

不管运行多少次,这个密码是规定的。因为这里没有用随机盐值。当密码长度很大,盐值也是随机的情况下,密码的强度也加大了。破解成本也增加了。

2. BCrypt 加密

  • Bcrypt 就是一款加密工具,可以比较方便地实现数据的加密工作。也可以简单理解为它内部自己实现了随机加盐处理 。
  • 使用MD5加密,每次加密后的密文其实都是一样的,这样就方便了MD5通过大数据的方式进行破解。
  • Bcrypt生成的密文是60位的,而MD5的是32位的,因此 Bcrypt 破解难度更大。

Bcrypt 的使用

(1)在 pom.xml 文件中添加依赖(添加到 <dependencies> </dependencies> 标签内)

<!-- security依赖包 (加密)-->
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-web</artifactId>
</dependency>
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-config</artifactId>
</dependency>

(2)在springboot启动类添加下面的内容

@SpringBootApplication(exclude ={org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})

(3)在 tools 包中创建 BCryptTest 测试类

package com.example.onlinemusic.tools;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;public class BCryptTest {public static void main(String[] args) {//模拟从前端获得的密码String password = "123456";BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();String newPassword = bCryptPasswordEncoder.encode(password);System.out.println("加密的密码为: "+newPassword);//使用matches方法进行密码的校验boolean same_password_result = bCryptPasswordEncoder.matches(password,newPassword);//返回trueSystem.out.println("加密的密码和正确密码对比结果: "+same_password_result);boolean other_password_result = bCryptPasswordEncoder.matches("987654",newPassword);//返回falseSystem.out.println("加密的密码和错误的密码对比结果: " + other_password_result);}
}

运行结果(每次运行的生成的密码都不一样)

  • encode方法:对用户密码进行加密。
  • matches方法:参数一,待检验的未加密的密码 。参数二:从数据库中查询出的加密后密码 。

3. BCrypt加密与MD5加密的区别:

  • BCrypt加密: 一种加盐的单向Hash,不可逆的加密算法,同一种明文(plaintext),每次加密后的密文都不一样,而且不可反向破解生成明文,破解难度很大。
  • MD5加密: 是不加盐的单向Hash,不可逆的加密算法,同一个密码经过hash的时候生成的是同一个hash值,在大多数的情况下,有些经过md5加密的方法将会被破解。
  • Bcrypt生成的密文是60位的。而MD5的是32位的。
  • 目前,MD5和BCrypt比较流行。相对来说,BCrypt比MD5更安全,但加密更慢。
  • 虽然BCrpyt也是输入的字符串+盐,但是与MD5+盐的主要区别是:每次加的盐不同(BCrpyt 中的盐值是随即生成的),导致每次生成的结果也不相同。无法比对!

六、配置拦截器

  • 未登录的情况下拦截其他页面,登录成功后才可以访问其他界面

1、创建 config 包,在 config 包中创建 LoginInterceptor 类

package com.example.onlinemusic.config;import com.example.onlinemusic.tools.Constant;
import com.example.onlinemusic.tools.ResponseBodyMessage;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HttpSession session = request.getSession(false);if(session == null || session.getAttribute(Constant.USER_SESSION_KEY)==null){return false;}return true;}
}

2、在 config 包中创建 AppConfig 类

package com.example.onlinemusic.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class AppConfig implements WebMvcConfigurer {@Beanpublic BCryptPasswordEncoder getBCryptPasswordEncoder(){return  new BCryptPasswordEncoder();}/*** 添加拦截器* @param registry*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 登录之后才可以访问其他页面LoginInterceptor loginInterceptor = new LoginInterceptor();registry.addInterceptor(loginInterceptor).// 拦截所有的addPathPatterns("/**")//排除所有的JS.excludePathPatterns("/js/**.js")//排除images下所有的元素.excludePathPatterns("/images/**").excludePathPatterns("/css/**.css").excludePathPatterns("/fronts/**").excludePathPatterns("/player/**").excludePathPatterns("/login.html").excludePathPatterns("/register.html")//排除登录和注册接口.excludePathPatterns("/user/login").excludePathPatterns("/user/register");}
}

七、实现登录模块

1、登录功能的请求和响应设计

请求:
{post, // 使用 post 请求/user/login // 请求路径data:{ username, password } // 传入的数据
}响应:
{"status": 200,"message": "登录成功","data": {"id": xxxxx,"username": xxxxxx,"password": xxxxxxxx}}响应设计字段解释:
{状态码为 200 表示成功,-200表示失败状态描述信息,描述此次请求成功或者失败的原因返回的数据,请求成功后,需要给前端的数据信息(返回用户id,用户名)
} 

2、创建 User 类

  • 在 package com.example.musicserver.model 包中创建User类
package com.example.onlinemusic.model;import lombok.Data;@Data
public class User {private int userId; // 用户idprivate String username; // 用户名private String password; // 密码
}

3、创建对应的 Mapper 和 Controller

1. 创建接口 UserMapper

  • 在 package com.example.musicserver.mapper 包中创建 UserMapper 接口

package com.example.onlinemusic.mapper;import com.example.onlinemusic.model.User;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface UserMapper {User login(User loginUser);
}

2. 创建 UserMapper.xml

  • 在resource目录下,新建 mybatis 文件夹,新建 UserMapper.xml,在 UserMapper.xml 文件中添加配置。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.onlinemusic.mapper.UserMapper"><select id="login" resultType="com.example.onlinemusic.model.User">select * from user where username=#{username} and password=#{password}</select><select id="selectByName" resultType="com.example.onlinemusic.model.User">select * from user where username=#{username};</select></mapper>

4、在 UserMapper 接口中新增 selectByName 方法

// 通过用户名查询用户是否存在(用户名是唯一的)
User selectByName(String username); 

5、创建 UserController 类

  • 在 package com.example.musicserver.controller 包下,创建 UserController 类
package com.example.onlinemusic.controller;import com.example.onlinemusic.mapper.UserMapper;
import com.example.onlinemusic.model.User;
import com.example.onlinemusic.tools.Constant;
import com.example.onlinemusic.tools.ResponseBodyMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserMapper userMapper;// 使用 BCrypt 对密码进行加密@Autowiredprivate BCryptPasswordEncoder bCryptPasswordEncoder;@RequestMapping("/login")// 传入用户名和密码public ResponseBodyMessage<User> login(@RequestParam String username, @RequestParam String password, HttpServletRequest request){// 调用 UserMapper 接口User user = userMapper.selectByName(username);// 判断是否登录成功if(user != null){System.out.println("登录成功"); // 可以在控制台中打印登录信息// 判断当前用户输入的密码(password) 与 数据库中查询到的密码(加密的密码,getPassword())是否匹配boolean flag = bCryptPasswordEncoder.matches(password,user.getPassword());if(!flag){// 密码不匹配,登录失败return new ResponseBodyMessage<>(-200,"用户名或密码错误",user);}// 如果登录成功就将信息写入到 session 中(在 session 中存储了一个用户信息对象,此后可以随时从 session 中将这个对象取出来进行一些操作)request.getSession().setAttribute(Constant.USER_SESSION_KEY,user);// 状态码为200,表示登录成功,并返回用户信息return   new ResponseBodyMessage<>(200,"登录成功",user);}else{System.out.println("登录失败");// 状态码为500,表示登录失败,并返回用户信息return   new ResponseBodyMessage<>(-200,"用户名或密码错误",user);}}
}

6、登录成功测试

  • 在数据库中插入一条数据,启动项目,使用 postman 进行测试。

7、前端代码

    <script>$(function(){$("#submit").click(function(){// 点击登录按钮,获取用户名和密码var username = $("#username").val();var password = $("#password").val();// 判断用户名和密码是否为空(使用 trim 方法,防止输入空格)if(username.trim() == "" || password.trim() == ""){alert("账号或密码不能为空");return;}// 如果用户名和密码不为空,使用 Ajax 传入请求$.ajax({type:"POST",url:"/user/login",data:{"username":username,"password":password},// 服务器返回的数据类型dataType:"json",// 请求成功,服务器返回数据success:function(data){console.log(data);// 如果状态码为 200,表示登录成功if(data.status == 200){alert("登录成功");// 跳转到指定页面window.location.href="list.html";}else{alert("登录失败,账号或密码错误");// 登录失败,将用户名或密码置空$("#username").val("");$("#password").val("");}}});});});$(function () {$("#register").click(function () {window.location.href="register.html";});});</script>

八、实现注册模块

1、注册功能的请求和响应设计

请求:
{post, // 使用 post 请求/user/register // 请求路径data:{ username, password } // 传入的数据
}响应:
{"status": 200,"message": "注册成功","data": {"id": xxxxx,"username": xxxxxx,"password": xxxxxxxx}}响应设计字段解释:
{状态码为 200 表示成功,-200表示失败状态描述信息,描述此次请求成功或者失败的原因返回的数据,请求成功后,需要给前端的数据信息(返回用户id,用户名)
} 

2、在 UserMapper 接口新增方法

    // 输入用户名和密码,注册账号boolean insertInToValues(String username,String password);

3、UserMapper.xml 文件中添加代码

    <insert id="insertInToValues" >insert into user(username,password)values(#{username},#{password});</insert>

4、在 UserController 类中添加 register 方法

    /*** 用户注册* @param username* @param password* @return*/@RequestMapping("/register")public ResponseBodyMessage<Boolean> register(@RequestParam String username,@RequestParam String password) {User user1 = userMapper.selectByName(username);if(user1 != null) {return new ResponseBodyMessage<>(-1,"当前用户已经存在",false);}else {String newPassword = bCryptPasswordEncoder.encode(password);boolean flag = userMapper.insertInToValues(username,newPassword);if(flag == true){return new ResponseBodyMessage<>(200,"注册成功",true);}else{return new ResponseBodyMessage<>(-200,"注册失败",false);}}}

5、注册功能测试

6、前端代码

    <script>$(function(){$("#register").click(function(){var username = $("#username").val();var password = $("#password").val();$.ajax({url: "/user/register",type: "POST",data:{"username":username,"password":password},dataType:"json",success: function(data){console.log(data);if(data.status == 200) {alert("注册成功");window.location.href="login.html";}else{alert("注册失败");$("#username").val("");$("#password").val("");$("#repassword").val("");}}})})});</script>

九、实现上传音乐模块

1、上传音乐功能的请求和响应设计

请求:
{post, // 使用 post 请求/music/upload // 请求路径{singer,MultipartFile file},//上传歌手,歌曲文件
} 响应:
{"status": 200, "message": "上传成功!","data": true
}响应设计字段解释:
{状态码为 200 表示成功,-200 表示失败状态描述信息,描述此次请求成功或者失败的原因返回的数据,请求成功后,需要给前端的数据信息,true 表示上传成功,false 表示上传失败
} 

2、创建 Music 类

  • 在 package com.example.musicserver.model 包中创建 Music 类
package com.example.onlinemusic.model;import lombok.Data;@Data
public class Music {private int musicId; // 歌曲idprivate String title; // 歌曲名称private String singer; //歌手private String time; // 上传歌曲的时间private String url; // 上传歌曲的路径private int userId; // 上传歌曲的用户
}

3、创建接口 MusicMapper

  • 在 package com.example.musicserver.mapper 包中创建 MusicMapper接口
package com.example.onlinemusic.mapper;import org.apache.ibatis.annotations.Mapper;@Mapper
public interface MusicMapper {/***  插入音乐* @param title* @param singer* @param time* @param url* @param userId* @return*/int insert(String title,String singer,String time,String url,int userId);/***  查询歌曲名* @param title* @return*/List<Music> selectBytitle(String title);
}

4、创建 MusicMapper.xml

  • 在 package resources.mybatis 下创建 MusicMapper.xml 文件,在 MusicMapper.xml 文件中添加配置。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.onlinemusic.mapper.UserMapper"><insert id="insert">insert into music(title,singer,time,url,userId)values(#(title),#(singer),#(time),#(url),#(userId));</insert><select id="selectBytitle" resultType="com.example.onlinemusic.model.Music">select * from music where title = #{title};</select></mapper>

5、创建 MusicController 类

  • 在 package com.example.musicserver.controller 包下,创建 MusicController 类
package com.example.onlinemusic.controller;import com.example.onlinemusic.mapper.LoveMusicMapper;
import com.example.onlinemusic.mapper.MusicMapper;
import com.example.onlinemusic.model.Music;
import com.example.onlinemusic.model.User;
import com.example.onlinemusic.tools.Constant;
import com.example.onlinemusic.tools.ResponseBodyMessage;
import org.apache.ibatis.binding.BindingException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;@RestController
@RequestMapping("/music")
public class MusicController {// 在配置文件中添加歌曲路径@Value("${music.local.path}")private String SAVE_PATH;@Autowiredprivate MusicMapper musicMapper;@Autowiredprivate LoveMusicMapper loveMusicMapper;/***  上传音乐*  请求路径:/music/upload* @param singer 上传歌手* @param file 上传歌曲* @param request 请求,验证是否登录* @return 返回true表示上传成功,返回false表示上传失败*/@RequestMapping("/upload")public ResponseBodyMessage<Boolean> insertMusic(@RequestParam String singer, @RequestParam ("filename")MultipartFile file, HttpServletRequest request, HttpServletResponse response){// 1. 检查是否登录HttpSession session = request.getSession(false);if(session == null || session.getAttribute(Constant.USER_SESSION_KEY)==null){System.out.println("没有登录");return  new ResponseBodyMessage<>(-200,"请登录后再进行上传",false);}// 2. 获取的是文件的完整名称,包括文件名称+文件拓展名String fileNameAndType = file.getOriginalFilename();// 3. 查询数据库中是否存在当前要上传的音乐(歌曲名+歌手)/***  获取标题(标题不包含后缀.mp3)*  使用 lastIndexOf 从后向前找第一个 .*/int index = fileNameAndType.lastIndexOf(".");String title = fileNameAndType.substring(0,index);// 使用 list 存放歌曲,获取歌曲名List<Music> list = musicMapper.selectBytitle(title);if(list != null){for(Music music : list){// 判断当前上传的歌曲+歌手在数据库中是否存在,如果存在则上传失败(歌曲名+歌手 不能重复)if(music.getSinger().equals(singer)){return new ResponseBodyMessage<>(-200,"上传失败,数据库中存在此歌曲,不能重复上传",false);}}}// 2. 数据上传到服务器// 上传文件路径String path = SAVE_PATH+fileNameAndType;// 上传文件File dest = new File(path);if(!dest.exists()){//如果路径不存在就创建目录dest.mkdir();}try {// 将接收到的文件传输到给定目标路径file.transferTo(dest);} catch (IOException e) {e.printStackTrace();return new ResponseBodyMessage<>(-200,"上传失败,服务器出现问题",false);}// 3. 判断上传的文件是否为mp3文件(判断是否存在 TAG 字符)File file1 = new File(path);byte[] bytes = null;try {bytes = Files.readAllBytes(file1.toPath());if(bytes == null){return new ResponseBodyMessage<>(-200,"上传失败,文件不存在",false);}String str = new String(bytes);if(!str.contains("TAG")){file1.delete();return new ResponseBodyMessage<>(-200,"上传的文件不是mp3文件",false);}} catch (IOException e) {e.printStackTrace();return new ResponseBodyMessage<>(-200,"上传失败,服务器出现问题",false);}// 4. 将数据上传到数据库中(1. 准备数据      2. 调用 insert)/***  获取 userId* 登录成功后将用户信息写到 session 中,通过 session 中key值(Constant.USERINFO) 就可以获取到对应的 value 值(用户信息)*/User user = (User)session.getAttribute(Constant.USER_SESSION_KEY);// 获取用户Idint userId = user.getUserId();/***  url 的作用: 播放音乐->发送 http 请求*/String url = "/music/get?path="+title; // 将 url 存入数据库时不用加后缀 .mp3,在取数据的时候加一个后缀就可以了/***  获取上传的时间*  将获取的时间格式化为:年-月-日 的形式*/SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");String time = simpleDateFormat.format(new Date());// 插入数据try {int ret = musicMapper.insert(title,singer,time,url,userId);if(ret == 1){// 数据插入成功// 这里应该跳转到音乐列表页面response.sendRedirect("/list.html");return new ResponseBodyMessage<>(200,"数据库上传成功",true);}else{// 数据插入失败return new ResponseBodyMessage<>(-200,"数据库上传失败",false);}}catch (BindingException | IOException e){// 数据库上传失败,将上传到文件夹中的数据删除dest.delete();e.printStackTrace();return new ResponseBodyMessage<>(-200,"数据库上传失败",false);}}/***  播放音乐*  请求路径:/music/get?get=xxx.mp3* @param path* @return*/@RequestMapping("/get")public ResponseEntity<byte[]> playMusic(@RequestParam String path) {File file = new File(SAVE_PATH+path);byte[] bytes = null;try {bytes  = Files.readAllBytes(file.toPath()); // 将文件路径中的文件以字节的形式读取,放到 bytes 数组中if(bytes == null){// 如果没有读取的文件,则返回状态码 400return ResponseEntity.badRequest().build();}// 成功读取到文件return ResponseEntity.ok(bytes);} catch (IOException e) {e.printStackTrace();}// 如果没有读取的文件,则返回状态码 400return ResponseEntity.badRequest().build();}/*** 删除单个音乐* 请求路径:/music/delete?musicId=x* @param musicId* @return*/@RequestMapping("/delete")public ResponseBodyMessage<Boolean> deleteMusicByMusicId(@RequestParam String musicId){/*** 1. 检查待删除的音乐是否存在* 2. 如果存在要删除的音乐*      1. 删除数据库中的数据*      2. 删除服务器上的数据*/// 检查待删除的音乐是否存在Music music = musicMapper.findMusicById(Integer.parseInt(musicId));if(music == null){System.out.println("在控制台打印日志:没有要删除的音乐id");return new ResponseBodyMessage<>(-200,"要删除的音乐不存在",false);}else{// 调用 musicMapper 接口中的 deleteMusicById 方法删除数据库中的数据int ret = musicMapper.deleteMusicById(Integer.parseInt(musicId));if(ret == 1){// 成功删除数据库中的数据/*int index = music.getUrl().lastIndexOf("=");String fileName = music.getUrl().substring(index+1);*/String fileName = music.getTitle();// 根据存放音乐的路径删除服务器中的数据File file = new File(SAVE_PATH+fileName+".mp3");System.out.println("在控制台打印日志:当前音乐的路径:"+file.getPath());//对删除服务器中的数据进行判断if(file.delete()){// 删除成功return new ResponseBodyMessage<>(200,"音乐删除成功",true);}else{return new ResponseBodyMessage<>(-200,"服务器中的音乐删除失败",false);}}else{return new ResponseBodyMessage<>(-200,"数据库中的音乐删除失败",false);}}}/***  批量删除选中的音乐*  请求路径:/music/deleteSel* @param musicId* @return*/@RequestMapping("/deleteSel")public ResponseBodyMessage<Boolean> deleteSelMusic(@RequestParam("musicId[]") List<Integer> musicId) {System.out.println("在控制台打印日志:所有音乐的 Id:"+musicId);int sum = 0; // 统计删除的音乐for (int i = 0; i < musicId.size(); i++) {Music music = musicMapper.findMusicById(musicId.get(i));if (music == null) {System.out.println("没有要删除的音乐id");return new ResponseBodyMessage<>(-200, "要删除的音乐不存在", false);}int ret = musicMapper.deleteMusicById(musicId.get(i));if (ret == 1) {// 成功删除数据库中的数据String fileName = music.getTitle();// 根据存放音乐的路径删除服务器中的数据File file = new File(SAVE_PATH + fileName + ".mp3");System.out.println("当前音乐的路径:" + file.getPath());//对删除服务器中的数据进行判断if (file.delete()) {// 成功删除一条数据,sum 就加上 ret(数据库中成功删除)sum += ret;} else {return new ResponseBodyMessage<>(-200, "服务器中的音乐删除失败", false);}}else{return new ResponseBodyMessage<>(-200,"数据库中的音乐删除失败",false);}}if(sum == musicId.size()){// 选中的数据全部删除成功System.out.println("在控制台打印日志:选择中的歌曲删除成功");return new ResponseBodyMessage<>(200,"音乐删除成功",true);}else{System.out.println("在控制台打印日志:选择中的歌曲删除失败");return new ResponseBodyMessage<>(-200,"音乐删除失败",false);}}/***  查询音乐* @param musicName* @return*/@RequestMapping("/findmusic")public ResponseBodyMessage<List<Music>> findMusic(@RequestParam(required = false) String musicName){List<Music> musicList = null;if(musicName != null){// 模糊查询,根据歌曲名查询指定的歌曲musicList = musicMapper.findMusicByName(musicName);}else{// 查询所有的音乐musicList = musicMapper.findMusic();}// 查询成功,返回查询到的音乐信息return new ResponseBodyMessage<>(200,"查询成功",musicList);}
}

6、上传音乐功能测试

7、前端代码

    <div class="upload-container"><form method="post" enctype="multipart/form-data" action="/music/upload"><div class="upload-dialog"><strong>上传本地音乐</strong><em>XiaoXiangYeYu's music upload_music</em><div class="row"><span>音乐</span><input type="file" id="file" name="filename" placeholder="上传歌曲" class="upload_txtbx"/></div><div class="row"><span>歌手</span><input type="text" id="singer" name="singer" placeholder="请输入歌手名" class="upload_txtbx"/></div><div class="row"><input type="submit" id="submit" value="上传歌曲" class="submit_btn"/></div></div></form></div>

十、实现播放音乐模块

1、播放音乐功能的请求和响应设计

请求:
{get, // 使用 get 请求/music/get?path=xxx.mp3 // 请求路径(数据库中存储的 url)
} 响应:
{音乐数据本身的字节信息 // 服务器将数据以字节的形式返回给客户端,客户端获取到信息后就可以进行解析,然后播放音乐
}

2、在 MusicController 类中添加 playMusic 方法

   /*** 播放音乐* 请求路径:/music/get?paht=xxx.mp3* @param path* @return*/@RequestMapping("/get")public ResponseEntity<byte[]> playMusic(@RequestParam String path) {File file = new File(SAVE_PATH+path);byte[] bytes = null;try {bytes  = Files.readAllBytes(file.toPath()); // 将文件路径中的文件以字节的形式读取,放到 bytes 数组中if(bytes == null){// 如果没有读取的文件,则返回状态码 400return ResponseEntity.badRequest().build();}// 成功读取到文件return ResponseEntity.ok(bytes);} catch (IOException e) {e.printStackTrace();}// 如果没有读取的文件,则返回状态码 400return ResponseEntity.badRequest().build();}

3、播放音乐功能测试

4、前端代码

<script>var audios = document.getElementsByTagName("audio");// 暂停函数function pauseAll() {var self = this;[].forEach.call(audios, function (i) {// 将audios中其他的audio全部暂停i !== self && i.pause();})}// 给play事件绑定暂停函数[].forEach.call(audios, function (i) {i.addEventListener("play", pauseAll.bind(i));});
</script>

十一、实现删除音乐模块

1、删除单个音乐

1. 删除单个音乐的请求和响应设计

请求:
{post, // 使用 post 请求/music/delete, // 请求路径musicId // 要删除歌曲的 id
} 响应:
{"status": 200,"message": "删除成功!","data": true
}响应设计字段解释:
{状态码为 200 表示成功,-200 表示失败状态描述信息,描述此次请求成功或者失败的原因返回的数据,请求成功后,需要给前端的数据信息,true 表示删除成功,false 表示删除失败
} 

2. 在 MusicMapper 接口中添加代码

    /*** 通过音乐 Id 查询当前音乐是否存在* @param musicId* @return*/Music findMusicById(int musicId);/*** 通过当前音乐 Id 删除音乐* @param musicId* @return*/int deleteMusicById(int musicId);

3. MusicMapper.xml 文件中添加代码

    <select id="findMusicById" resultType="com.example.onlinemusic.model.Music">select * from music where musicid = #{musicid};</select><delete id="deleteMusicById" parameterType="java.lang.Integer">delete from music where musicid = #{musicid};</delete>

4. 在 MusicController 类中添加 deleteByMusicId 方法

    /*** 删除单个音乐* 请求路径:/music/delete?musicId=x* @param musicId* @return*/@RequestMapping("/delete")public ResponseBodyMessage<Boolean> deleteByMusicId(@RequestParam String musicId){/*** 1. 检查待删除的音乐是否存在* 2. 如果存在要删除的音乐*      1. 删除数据库中的数据*      2. 删除服务器上的数据*/// 检查待删除的音乐是否存在Music music = musicMapper.findMusicById(Integer.parseInt(musicId));if(music == null){System.out.println("在控制台打印日志:没有要删除的音乐id");return new ResponseBodyMessage<>(-200,"要删除的音乐不存在",false);}else{// 调用 musicMapper 接口中的 deleteMusicById 方法删除数据库中的数据int ret = musicMapper.deleteMusicById(Integer.parseInt(musicId));if(ret == 1){// 成功删除数据库中的数据String fileName = music.getTitle();// 根据存放音乐的路径删除服务器中的数据File file = new File(SAVE_PATH+fileName+".mp3");System.out.println("在控制台打印日志:当前音乐的路径:"+file.getPath());//对删除服务器中的数据进行判断if(file.delete()){// 删除成功return new ResponseBodyMessage<>(200,"音乐删除成功",true);}else{return new ResponseBodyMessage<>(-200,"服务器中的音乐删除失败",false);}}else{return new ResponseBodyMessage<>(-200,"数据库中的音乐删除失败",false);}}}

5. 删除单个音乐功能测试

6. 前端代码

        function deleteInfo(obj){console.log(obj);$.ajax({url:"/music/delete",type:"POST",data:{"musicId":obj},dataType:"json",success:function(data){console.log(data);if(data.data == true){alert("删除成功,重新加载当前页面");window.location.href = "list.html";}else{alert("删除失败");}}});}

2、批量删除选中的音乐

1. 批量删除选中的音乐的请求和响应设计

请求:
{post, // 使用 post 请求/music/deleteSel, // 请求路径data:{"id":musicId // 要删除的歌曲 id 的数组}
} 响应:
{"status": 200,"message": "批量删除成功!","data": true
}响应设计字段解释:
{状态码为 200 表示成功,-200 表示失败状态描述信息,描述此次请求成功或者失败的原因返回的数据,请求成功后,需要给前端的数据信息,true 表示删除成功,false 表示删除失败
} 

2. 在 MusicController 类中添加 deleteSelMusic 方法

    /***  批量删除选中的音乐*  请求路径:/music/deleteSel* @param musicId* @return*/@RequestMapping("/deleteSel")public ResponseBodyMessage<Boolean> deleteSelMusic(@RequestParam("musicId[]") List<Integer> musicId) {System.out.println("在控制台打印日志:所有音乐的 Id:"+musicId);int sum = 0; // 统计删除的音乐for (int i = 0; i < musicId.size(); i++) {Music music = musicMapper.findMusicById(musicId.get(i));if (music == null) {System.out.println("没有要删除的音乐id");return new ResponseBodyMessage<>(-200, "要删除的音乐不存在", false);}int ret = musicMapper.deleteMusicById(musicId.get(i));if (ret == 1) {// 成功删除数据库中的数据String fileName = music.getTitle();// 根据存放音乐的路径删除服务器中的数据File file = new File(SAVE_PATH + fileName + ".mp3");System.out.println("当前音乐的路径:" + file.getPath());//对删除服务器中的数据进行判断if (file.delete()) {// 成功删除一条数据,sum 就加上 ret(数据库中成功删除)sum += ret;} else {return new ResponseBodyMessage<>(-200, "服务器中的音乐删除失败", false);}}else{return new ResponseBodyMessage<>(-200,"数据库中的音乐删除失败",false);}}if(sum == musicId.size()){// 选中的数据全部删除成功System.out.println("在控制台打印日志:整体删除成功");return new ResponseBodyMessage<>(200,"音乐删除成功",true);}else{System.out.println("在控制台打印日志:整体删除失败");return new ResponseBodyMessage<>(-200,"音乐删除失败",false);}}
}

3. 批量删除选中的音乐功能测试

4. 前端代码

 $(function(){$("#submit1").click(function(){var name = $("#exampleInputName2").val();load(name);// window.location.href = "findMusic?musicName="+name;});$.when(load).done(function(){$("#delete").click(function(){var id = new Array(); // 音乐Idvar i = 0; // 数组下标// 遍历checkbox$("input:checkbox").each(function(){// 如果被选中,this代表发生事件的dom元素,<input>if($(this).is(":checked")){id[i] = $(this).attr("id");i++;}});console.log(id);$.ajax({url:"/music/deleteSel",data:{"musicId":id},dataType:"json",type:"POST",success:function(obj){if(obj.data == true){alert("删除成功");window.location.href = "list.html";}else{alert("删除失败");}}});});});});

十二、实现查询音乐模块

1、查询音乐的请求和响应设计

此处查询需要满足两个功能:

  1. 支持模糊查询
  2. 支持传入参数为空,当参数为空时默认查询到所有的音乐
请求:
{get, // 使用 get 请求/music/findmusic, // 请求路径data:{musicName:musicName}, // 根据歌曲名进行查询
} 响应:【不给musicName传参】// 如果不传参时默认查询到所有的音乐
{"status": 200,"message": "查询到了歌曲的信息","data": [{"id": 19,"title": "张靓颖 - 我的梦","singer": "张靓颖","url": "/music/get?path=张靓颖 - 我的梦","time": "2022-08-20","userid": 1},{"id": 20,"title": "纯音乐 - Victory","singer": "张三","url": "/music/get?path=纯音乐 - Victory","time": "2022-03-20","userid": 1}]
} 响应:【给musicName传参】// 如果传入参数返回指定查询的歌曲
{"status": 200,"message": "查询到了歌曲的信息","data": [{"id": 19,"title": "张靓颖 - 我的梦","singer": "张靓颖","url": "/music/get?path=张靓颖 - 我的梦","time": "2022-08-20","userid": 1}]
}响应设计字段解释:
{状态码为 200 表示成功状态描述信息,描述此次请求成功返回的数据,请求成功后给前端的数据信息,返回查询到的音乐信息(歌曲id、歌曲名、歌手、存放歌曲的路径、上传时间、上传用户的id)
} 

2、在 MusicMapper 接口中添加代码

    /*** 查询所有的音乐* @return*/List<Music> findMusic();/*** 模糊查询,根据歌曲名查询指定的歌曲* @param musicName* @return*/List<Music> findMusicByName(String musicName);

3、MusicMapper.xml 文件中添加代码

    <select id="findMusic" resultType="com.example.onlinemusic.model.Music">select * from music ;</select><select id="findMusicByName" resultType="com.example.onlinemusic.model.Music">select * from music where title like concat('%', #{musicName}, '%');</select>

4、在 MusicController 类中添加 findMusic 方法

    /***  查询音乐* @param musicName* @return*/@RequestMapping("/findmusic")public ResponseBodyMessage<List<Music>> findMusic(@RequestParam(required = false) String musicName){List<Music> musicList = null;if(musicName != null){// 模糊查询,根据歌曲名查询指定的歌曲musicList = musicMapper.findMusicByName(musicName);}else{// 查询所有的音乐musicList = musicMapper.findMusic();}// 查询成功,返回查询到的音乐信息return new ResponseBodyMessage<>(200,"查询成功",musicList);}

5、查询音乐功能测试

1. 查询所有的音乐

2. 模糊匹配,查询指定的音乐

6、前端代码

    <script type="text/javascript">// 查询$(function(){load();});// musicName 可以传参(模糊匹配),也可以不传参(不传参默认传入的是所有的音乐)function load(musicName){$.ajax({type:"GET",url:"/music/findmusic",data:{"musicName":musicName},// 服务器返回的数据类型dataType:"json",// obj 查找存储的所有信息success:function(obj){console.log(obj);// data数组,存放歌曲信息var data = obj.data;var s = '';for(var i=0;i<data.length;i++){var musicUrl = data[i].url+".mp3";console.log(musicUrl);s += '<tr>';s += '<th> <input id="'+data[i].musicId+'"type="checkbox"> </th>';s += '<td>'+ data[i].title +'</td>';s += '<td>'+ data[i].singer +'</td>';s += "<td <a href=\"\">  <audio src= \""+ musicUrl+"\"  + controls=\"controls\" preload=\"auto\" loop=\"loop\" class=\"audio_btn\">  >"  + "</audio> </a> </td>";s += '<td> <button class = "btn btn-primary" onclick="loveInfo('+data[i].musicId+')"> 收藏歌曲 </button>' + '</td>';s += '<td> <button class="btn btn-primary" onclick="deleteInfo('+ data[i].musicId +')" >删除歌曲</button>'+'</td>';s += '</tr>';    }$("#info").html(s);}});}

十三、实现收藏音乐模块

1、收藏音乐的请求和响应设计

请求:
{post, // 使用 post 请求/lovemusic/likeMusic // 请求路径data: music_id //音乐id
} 响应:
{"status": 0,"message": "点赞音乐成功","data": true
}响应设计字段解释:
{状态码为 200 表示成功,-200 表示失败状态描述信息,描述此次请求成功或者失败的原因返回的数据,请求成功后,需要给前端的数据信息,true 表示收藏成功,false 表示收藏失败
} 

2、创建 LoveMusic 类

  • 在 package com.example.musicserver.model 包中创建 LoveMusic 类
package com.example.onlinemusic.model;import lombok.Data;@Data
public class LoveMusic {private int loveId;private int userId;private int musicId;
}

3、创建接口 LoveMusicMapper

  • 在 package com.example.musicserver.mapper 包中创建 LoveMusicMapper 接口
package com.example.onlinemusic.mapper;import com.example.onlinemusic.model.LoveMusic;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface LoveMusicMapper {/*** 查询是否已经收藏过该音乐* @param userId* @param musicId* @return*/LoveMusic findLoveMusic(int userId, int musicId);/*** 收藏音乐* @param userId* @param musicId* @return*/boolean insertLoveMusic(int userId,int musicId);}

4、创建 LoveMusicMapper.xml

  • 在 package resources.mybatis 下创建 LoveMusicMapper.xml 文件,在 LoveMusicMapper.xml 文件中添加配置。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.onlinemusic.mapper.LoveMusicMapper"><select id="findLoveMusic" resultType="com.example.onlinemusic.model.LoveMusic">select * from lovemusic where user_id = #{userId} and music_id = #{musicId};</select><insert id="insertLoveMusic">insert into lovemusic(user_id,music_id) values(#{userId}, #{musicId});</insert></mapper>

5、创建 LoveMusicController 类

  • 在 package com.example.musicserver.controller 包下,创建 LoveMusicController 类
package com.example.onlinemusic.controller;import com.example.onlinemusic.mapper.LoveMusicMapper;
import com.example.onlinemusic.model.LoveMusic;
import com.example.onlinemusic.model.Music;
import com.example.onlinemusic.model.User;
import com.example.onlinemusic.tools.Constant;
import com.example.onlinemusic.tools.ResponseBodyMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.List;@RestController
@RequestMapping("/lovemusic")
public class LoveMusicController {@Autowiredprivate LoveMusicMapper loveMusicMapper;/*** 收藏音乐* 请求路径:/lovemusic/likeMusic?music_id=x* @param music_id 传入的音乐 id* @param request* @return*/@RequestMapping("/likeMusic")public ResponseBodyMessage<Boolean> likeMusic(@RequestParam String music_id, HttpServletRequest request){// 1. 检查是否登录HttpSession session = request.getSession(false);if(session == null || session.getAttribute(Constant.USER_SESSION_KEY)==null){System.out.println("登录失败");return  new ResponseBodyMessage<>(-200,"请登录后再进行收藏",false);}// 2. 获取 usrIdUser user = (User)session.getAttribute(Constant.USER_SESSION_KEY);int userId = user.getUserId();System.out.println("在控制台打印日志:user_id:"+userId);// 3. 将传入的 music_id 转换成整型(获取 musicId)int musicId = Integer.parseInt(music_id);System.out.println("在控制台打印日志:music_id:"+musicId);// 4. 判断当前歌曲是否被收藏过LoveMusic loveMusic = loveMusicMapper.findLoveMusic(userId,musicId);if(loveMusic != null){// 之前收藏过这首歌曲return new ResponseBodyMessage<>(-200,"该歌曲已被收藏,请勿重复收藏",false);}else{boolean ret =  loveMusicMapper.insertLoveMusic(userId,musicId);if(ret){return new ResponseBodyMessage<>(200,"收藏成功",true);}else{return new ResponseBodyMessage<>(-200,"收藏失败",false);}}}
}

6、收藏音乐功能测试

7、前端代码

        function loveInfo(obj){$.ajax({type:"POST",url:"/lovemusic/likeMusic",data:{"music_id":obj},dataType:"json",success:function(data){if(data.data == true){alert("收藏成功");window.location.href = "loveMusic.html";}else{alert("收藏失败");}}});}

十四、实现查询收藏的音乐模块

1、查询收藏音乐的请求和响应设计

此处查询需要满足两个功能:

  1. 支持模糊查询
  2. 支持传入参数为空,当参数为空时默认查询到所有的音乐
请求:
{get, // 使用 get 请求/lovemusic/findlovemusic, // 请求路径data:{musicName:musicName}, // 根据歌曲名进行查询
} 响应:【不传入musicName参数】// 如果不传参时默认查询到所有的音乐
{"status": 200,"message": "查询到了所有的收藏的音乐","data": [{"id": 19,"title": "张靓颖 - 我的梦","singer": "张靓颖","url": "/music/get?path=张靓颖 - 我的梦","time": "2022-08-20","userid": 1},{"id": 20,"title": "纯音乐 - Victory","singer": "张三","url": "/music/get?path=纯音乐 - Victory","time": "2022-03-20","userid": 1}]
} 响应:【传入musicName参数】// 如果传入参数返回指定查询的歌曲
{"status": 200,"message": "查询到了收藏歌曲的信息","data": [{"id": 19,"title": "张靓颖 - 我的梦","singer": "张靓颖","url": "/music/get?path=张靓颖 - 我的梦","time": "2022-08-20","userid": 1}]
}响应设计字段解释:
{状态码为 200 表示成功状态描述信息,描述此次请求成功返回的数据,请求成功后给前端的数据信息,返回查询到的音乐信息(歌曲id、歌曲名、歌手、存放歌曲的路径、上传时间、上传用户的id)
} 

2、在 LoveMusicMapper 接口中添加代码

    /*** 查询当前用户收藏的所有音乐* @param userId* @return*/List<Music> findLoveMusicByUserId(int userId);/*** 模糊查询,根据歌曲名查询当前用户指定的收藏歌曲* @param musicName* @param userId* @return*/List<Music> findLoveMusicByMusicNameAndUserId(String musicName,int userId);

3、LoveMusicMapper.xml 文件中添加代码

    <select id="findLoveMusicByUserId" resultType="com.example.onlinemusic.model.Music">select music.*from music,lovemusicwhere music.musicid = lovemusic.music_id and user_id = #{userId};</select><select id="findLoveMusicByMusicNameAndUserId" resultType="com.example.onlinemusic.model.Music">select music.*from music,lovemusicwhere music.musicid = lovemusic.music_id and user_id = #{userId} and title like concat('%', #{musicName}, '%');</select>

4、在 LoveMusicController 类中添加 findLoveMusic 方法

    @RequestMapping("findlovemusic")public ResponseBodyMessage<List<Music>> findLoveMusic(@RequestParam(required = false) String musicName,HttpServletRequest request){// 1. 检查是否登录HttpSession session = request.getSession(false);if(session == null || session.getAttribute(Constant.USER_SESSION_KEY)==null){System.out.println("登录失败");return  new ResponseBodyMessage<>(-200,"请登录后再查找",null);}// 2. 获取 usrIdUser user = (User)session.getAttribute(Constant.USER_SESSION_KEY);int user_id = user.getUserId();System.out.println("在控制台打印日志:user_id:"+user_id);List<Music> musicList = null;if(musicName == null){//查询当前用户收藏的所有音乐musicList = loveMusicMapper.findLoveMusicByUserId(user_id);}else{//模糊查询,根据歌曲名查询当前用户指定的收藏歌曲musicList = loveMusicMapper.findLoveMusicByMusicNameAndUserId(musicName,user_id);}return new ResponseBodyMessage<>(200,"查询成功",musicList);}

5、查询收藏音乐功能测试

1. 查询所有的收藏音乐

2. 模糊匹配,查询指定的收藏音乐

6、前端代码

         $(function(){load();});// musicName 可以传参(模糊匹配),也可以不传参(不传参默认传入的是所有的音乐)function load(musicName){$.ajax({type:"GET",url:"/lovemusic/findlovemusic",data:{"musicName":musicName},// 服务器返回的数据类型dataType:"json",// obj 查找存储的所有信息success:function(obj){console.log(obj);// data数组,存放歌曲信息var data = obj.data;var s = '';for(var i=0;i<data.length;i++){var musicUrl = data[i].url+".mp3";s += '<tr>';s += '<td>'+ data[i].title +'</td>';s += '<td>'+ data[i].singer +'</td>';s += "<td <a href=\"\">  <audio src= \""+ musicUrl+"\"  + controls=\"controls\" preload=\"auto\" loop=\"loop\" class=\"audio_btn\">  >"  + "</audio> </a> </td>";s += '<td> <button class="btn btn-primary" onclick="deleteInfo('+ data[i].musicId +')">取消收藏</button>'+'</td>';s += '</tr>';    }$("#info").html(s);}});}

十五、实现取消收藏音乐模块

1、取消(移除)收藏音乐的请求和响应设计

请求:
{post, // 使用 post 请求/lovemusic/removelovemusic, // 请求路径data:{id:music_id} // 根据收藏列表中音乐的 id 进行移除
} 响应:
{"status": 200,"message": "取消收藏成功!","data": true
}响应设计字段解释:
{状态码为 200 表示成功,-200 表示失败状态描述信息,描述此次请求成功或者失败的原因返回的数据,请求成功后,需要给前端的数据信息,true 表示移除收藏成功,false 表示移除收藏失败
}

2、在 LoveMusicMapper 接口中添加代码

    /***  移除某个用户收藏的引用* @param userId 用户的 ID* @param musicId 待移除音乐的 ID* @return 受影响的行数*/int removeLoveMusic(int userId,int musicId);

3、LoveMusicMapper.xml 文件中添加代码

    <delete id="removeLoveMusic" parameterType="java.lang.Integer">delete from lovemusic where user_id = #{userId} and music_id = #{musicId};</delete>

4、在 LoveMusicController 类中添加 removeLoveMusic 方法

    /*** 移除收藏的音乐* @param music_id* @param request* @return*/@RequestMapping("/removelovemusic")public ResponseBodyMessage<Boolean> removeLoveMusic(@RequestParam String music_id,HttpServletRequest request){// 1. 检查是否登录HttpSession session = request.getSession(false);if(session == null || session.getAttribute(Constant.USER_SESSION_KEY)==null){System.out.println("登录失败");return  new ResponseBodyMessage<>(-200,"请登录后再移除",false);}// 2. 获取 usrIdUser user = (User)session.getAttribute(Constant.USER_SESSION_KEY);int userId = user.getUserId();System.out.println("在控制台打印日志:user_id:"+userId);//  3. 将传入的 music_id 转换成整型(获取 musicId)int musicId = Integer.parseInt(music_id);System.out.println("在控制台打印日志:music_id:"+musicId);int ret = loveMusicMapper.removeLoveMusic(userId,musicId);if(ret == 1){return new ResponseBodyMessage<>(200,"取消收藏成功",true);}else{return new ResponseBodyMessage<>(-200,"取消收藏失败",false);}}

5、取消收藏音乐功能测试

6、前端代码

       function deleteInfo(obj){console.log(obj);$.ajax({url:"/lovemusic/removelovemusic",type:"POST",data:{"music_id":obj},dataType:"json",success:function(data){console.log(data);if(data.data == true){alert("取消收藏成功,重新加载当前页面");window.location.href = "loveMusic.html";}else{alert("取消收藏失败");}}});}

十六、完善删除音乐功能

前面所完成的功能中存在一个BUG,当成功删除音乐表(music)中的音乐后,收藏表(lovemusic)中的音乐不会被删除。

1、在 LoveMusicMapper 接口中添加代码

    /*** 根据音乐的ID进行删除* @param musicId* @return*/int deleteLoveMusicByMusicId(int musicId);

2、LoveMusicMapper.xml 文件中添加代码

    <delete id="deleteLoveMusicByMusicId" parameterType="java.lang.Integer">delete from lovemusic where music_id = #{musicId};</delete>

3、调整 MusicController 类中的 deleteMusicByMusicId 和 deleteSelMusic 方法

// 在 MusicController 注入 LoveMusicMapper @Autowiredprivate LoveMusicMapper loveMusicMapper;// 调整后的 deleteMusicByMusicId 方法 和 deleteSelMusic 方法/*** 删除单个音乐* 请求路径:/music/delete?musicId=x* @param musicId* @return*/@RequestMapping("/delete")public ResponseBodyMessage<Boolean> deleteMusicByMusicId(@RequestParam String musicId){/*** 1. 检查待删除的音乐是否存在* 2. 如果存在要删除的音乐*      1. 删除数据库中的数据*      2. 删除服务器上的数据*/// 检查待删除的音乐是否存在Music music = musicMapper.findMusicById(Integer.parseInt(musicId));if(music == null){System.out.println("在控制台打印日志:没有要删除的音乐id");return new ResponseBodyMessage<>(-200,"要删除的音乐不存在",false);}else{// 调用 musicMapper 接口中的 deleteMusicById 方法删除数据库中的数据int ret = musicMapper.deleteMusicById(Integer.parseInt(musicId));if(ret == 1){// 成功删除数据库中的数据String fileName = music.getTitle();// 根据存放音乐的路径删除服务器中的数据File file = new File(SAVE_PATH+fileName+".mp3");System.out.println("在控制台打印日志:当前音乐的路径:"+file.getPath());//对删除服务器中的数据进行判断if(file.delete()){// 同步删除 lovemusic 表中的音乐loveMusicMapper.deleteLoveMusicByMusicId(Integer.parseInt(musicId));// 删除成功return new ResponseBodyMessage<>(200,"音乐删除成功",true);}else{return new ResponseBodyMessage<>(-200,"服务器中的音乐删除失败",false);}}else{return new ResponseBodyMessage<>(-200,"数据库中的音乐删除失败",false);}}}/***  批量删除选中的音乐*  请求路径:/music/deleteSel* @param musicId* @return*/@RequestMapping("/deleteSel")public ResponseBodyMessage<Boolean> deleteSelMusic(@RequestParam("musicId[]") List<Integer> musicId) {System.out.println("在控制台打印日志:所有音乐的 Id:"+musicId);int sum = 0; // 统计删除的音乐for (int i = 0; i < musicId.size(); i++) {Music music = musicMapper.findMusicById(musicId.get(i));if (music == null) {System.out.println("没有要删除的音乐id");return new ResponseBodyMessage<>(-200, "要删除的音乐不存在", false);}int ret = musicMapper.deleteMusicById(musicId.get(i));if (ret == 1) {// 成功删除数据库中的数据String fileName = music.getTitle();// 根据存放音乐的路径删除服务器中的数据File file = new File(SAVE_PATH + fileName + ".mp3");System.out.println("当前音乐的路径:" + file.getPath());//对删除服务器中的数据进行判断if (file.delete()) {// 同步删除 lovemusic 表中的音乐loveMusicMapper.deleteLoveMusicByMusicId(musicId.get(i));// 成功删除一条数据,sum 就加上 ret(数据库中成功删除)sum += ret;} else {return new ResponseBodyMessage<>(-200, "服务器中的音乐删除失败", false);}}else{return new ResponseBodyMessage<>(-200,"数据库中的音乐删除失败",false);}}if(sum == musicId.size()){// 选中的数据全部删除成功System.out.println("在控制台打印日志:选择中的歌曲删除成功");return new ResponseBodyMessage<>(200,"音乐删除成功",true);}else{System.out.println("在控制台打印日志:选择中的歌曲删除失败");return new ResponseBodyMessage<>(-200,"音乐删除失败",false);}}

4、功能测试

1. 查询已上传的音乐

2. 查询已收藏的音乐

3. 删除已上传的音乐(music 表中的数据)

4. 上传的音乐删除后,收藏的音乐也会被删除

基于 Spring boot + MyBatis 的在线音乐播放系统相关推荐

  1. 推荐一个基于 Spring Boot+MyBatis Plus+JWT 的问卷系统!

    你好呀,我是 Guide!这里是 JavaGuide 的「优质开源项目推荐」第 8 期,每一期我都会精选 5 个高质量的 Java 开源项目. 时间过的真快,不知不觉「优质开源项目推荐」系列已经持续半 ...

  2. 基于JAVA+SpringMVC+Mybatis+MYSQL的音乐播放系统

    项目功能: 系统包括管理员登录,歌曲管理,类别管理,歌手管理,点击量分析,退出登录,前台包括用户登录注册,查找歌曲,播放歌曲,收藏歌曲等功能 页面效果:

  3. 基于SpringBoot的在线音乐播放系统

    项目目录 一.项目概述 二.开发环境 三.系统搭建 四.用户功能 1.注册 2.登录 3.首页 4.歌单 5.歌手 6.收藏 7.搜索 8.播放 五.管理员功能 1.登录 2.系统首页 3.用户管理 ...

  4. Java在线音乐播放系统的设计与实现

    为了使用户可以在网络上在线欣赏音乐并且下载歌曲,方便用户对音乐资源的管理,设计并实现了该时光在线音乐播放系统.本时光在线音乐系统使用了Java web与B/S架构开发.通过实这个时光在线音乐播放系统, ...

  5. 基于Spring+SpringMVC+Mybatis开发电影院订票系统前后台

    你知道的越多,你不知道的越多 点赞再看,养成习惯 源码分享在文末,点赞关注,解锁更多毕业设计项目 企鹅:869192208 如果您有疑问或者见解,欢迎指教: 文章目录 一.开发背景 二. 需求分析 三 ...

  6. 在线音乐播放系统的设计与实现(论文+源码)_kaic

    摘  要 随着大城市建设步伐的加快,越来越多的市民在物质生活条件得到改善的同时,为了调节紧张的生活节奏,把目光投向了工作之余的休闲娱乐上.因此,娱乐市场需求越来越大.而其中,音乐更是休闲娱乐的主流.在 ...

  7. java项目-第99期基于spring+springmvc+hibernate的在线问卷答题系统-计算机毕业设计

    java项目-第99期基于spring+springmvc+hibernate的在线问卷答题系统[毕业设计] [源码请到下载专栏下载] 1.项目简述 今天分享的项目是<在线问卷答题系统>, ...

  8. 基于 SpringBoot + MyBatis 的在线音乐播放器

    文章目录 4. 数据库的设计与实现 6.3 了解 MD5 加密 和 BCrypt 加密 6.4 在Config中 注入 BCryptPasswordEncoder 对象 6.5.1 LoginInte ...

  9. 基于Spring+SpringMVC+Mybatis的分布式敏捷开发系统架构(附源码)

    点击上方 好好学java ,选择 星标 公众号重磅资讯,干货,第一时间送达 今日推荐:推荐19个github超牛逼项目!个人原创100W +访问量博客:点击前往,查看更多 作者:zheng gitee ...

最新文章

  1. Ghost后不能启动解决小工具
  2. 域名过期多长时间才能注册
  3. 洞悉物联网发展1000问之ZigbeePRO技术会卷土重来占领物联网吗
  4. 通过 Docker 部署 Redis 6.x 集群
  5. php的exportexcel,PHPExcel export网络或本地图片到excel
  6. ENVI帮助研究人员发现金矿
  7. mysql state_MySQL进程常见的State【转】
  8. 【Mac】mac安装redis客户端 Error: Cask ‘rdm‘ is unavailable: No Cask with this name exist
  9. 《Android测试的学习笔记》——Introduction
  10. WEB应用中的信息泄漏以及攻击方法
  11. python中实现上下文管理器的两种方法
  12. 网课题库接口(免费)高准确率
  13. ISO27000系列标准
  14. 北斗对时服务器(GPS卫星同步时钟)分析北斗与GPS区别
  15. Java大型CRM客户管理系统源码 带小程序 CRM小程序源码
  16. 导出的CSV数据中含有身份证并在Excel正确显示方法
  17. 我是怎么学英语的(四级没过如何突破听说读写)
  18. 音频测量常见的校准原理 ADC灵敏度校准 DAC频率响应校准 麦克风校准 Soundcheck软件校准
  19. python作排产计划_轻松打造11周精通python计划(完结) | 软件库
  20. 商业需求文档该如何去写?

热门文章

  1. 电脑照片怎么导入苹果手机?三个妙招帮你解决!
  2. Cesium 三维热力图
  3. MongoDB Capped Collections
  4. 注意:18种会致流产或让宝宝变傻的食物
  5. NAT 原理与NAT穿越
  6. 腾讯QQ服务器汇总表
  7. JavaScript 二维数组转一维数组
  8. 学习软件测试(三)测试用例、测试用例的设计方法(等价类划分法、边界值分析法、判定表法、因果图法、正交排列法、场景法、错误推测法)
  9. 错失过良机的多点Dmall,如今还能获得多少青睐?
  10. MySQL_08_触发器