0


基于 SpringBoot + Mybatis 的个人在线音乐平台

1. 核心功能

用到的技术:
前端 :

HTML + CSS + JavaScript + JQuery

后端 :

Spring Boot + MyBatis

在这里插入图片描述

2. 演示效果

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

源码地址:https://gitee.com/big-white-rice/online-music-platform

3. 创建项目

我使用的 IDEA 工具
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4. 数据库设计

我使用的

MySQL 5.5

在这里插入图片描述

-- 数据库dropdatabaseifexists`onlinemusic`;createdatabaseifnotexists`onlinemusic`characterset utf8;-- 使用数据库use`onlinemusic`;DROPTABLEIFEXISTS`user`;CREATETABLE`user`(`id`INTPRIMARYKEYAUTO_INCREMENT,`username`varchar(20)NOTNULL,`password`varchar(255)NOTNULL);INSERTINTOuser(username,password)VALUES("cm","$2a$10$Bs4wNEkledVlGZa6wSfX7eCSD7wRMO0eUwkJH0WyhXzKQJrnk85li");DROPTABLEIFEXISTS`music`;CREATETABLE`music`(`id`intPRIMARYKEYAUTO_INCREMENT,`title`varchar(50)NOTNULL,`singer`varchar(30)NOTNULL,`time`varchar(13)NOTNULL,`url`varchar(1000)NOTNULL,`userid`int(11)NOTNULL);DROPTABLEIFEXISTS`lovemusic`;CREATETABLE`lovemusic`(`id`intPRIMARYKEYAUTO_INCREMENT,`user_id`int(11)NOTNULL,`music_id`int(11)NOTNULL);

5. 配置数据库和xml

application.properties

中,常用的可以保存在一个地方,方便下次直接粘贴

#配置数据库
spring.datasource.url=jdbc:mysql://localhost:3306/onlinemusic?characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

#启动端口
server.port=8082

#配置xml
mybatis.mapper-locations=classpath:mybatis/**Mapper.xml

#配置springboot上传文件的大小,默认每个文件的配置最大为15Mb,单次请求的文件的总数不能大于100Mb
spring.servlet.multipart.max-file-size = 15MB
spring.servlet.multipart.max-request-size=100MB

#音乐上传后的路径
music.local.path=D:/Java/Music/

#music.local.path=/root/music/

# 配置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
#package com.example.onlinemusic.model;
#import lombok.Data;

mybatis xml 文件,保留备用

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="com.example.onlinemusic.mapper.UserMapper"></mapper>

6. 统一配置类

6.1 设置统一响应类

统一让响应的返回格式一样

importlombok.Data;@DatapublicclassResponseBodyMessage<T>{privateint status;// 状态码privateString message;// 返回的信息(出错的原因? 没错的原因?)privateT data;// 返回前端的数据publicResponseBodyMessage(int status,String message,T data){this.status = status;this.message = message;this.data = data;}}

6.2 Constant 类

存储不变的常量,方便我们后续使用(如 session)

publicclassConstant{publicstaticfinalString USERINFO_SESSION_KEY ="USERINFO_SESSION_KEY";}

6.3 登录加密(了解)

6.3.1 MD5 加密

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

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

不安全的原因:

  1. 暴力攻击速度很快
  2. 字典表很大
  3. 碰撞

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

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

添加依赖:

<!-- 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>

测试类:

importorg.apache.commons.codec.digest.DigestUtils;publicclassMD5Util{//定义一个固定的盐值privatestaticfinalString salt ="1b2i3t4e";publicstaticStringmd5(String src){returnDigestUtils.md5Hex(src);}/**
            * 第一次加密 :模拟前端自己加密,然后传到后端
            * @param inputPass
            * @return
            */publicstaticStringinputPassToFormPass(String inputPass){String str =""+salt.charAt(1)+salt.charAt(3)+ inputPass
                +salt.charAt(5)+ salt.charAt(6);returnmd5(str);}/**
            * 第2次MD5加密
            * @param formPass 前端加密过的密码,传给后端进行第2次加密
            * @param salt 用户数据库当中的盐值
            * @return
            */publicstaticStringformPassToDBPass(String formPass,String salt){String str =""+salt.charAt(0)+salt.charAt(2)+ formPass +salt.charAt(5)+ salt.charAt(4);returnmd5(str);}/**
            * 上面两个函数合到一起进行调用
            * @param inputPass
            * @param saltDB
            * @return
            */publicstaticStringinputPassToDbPass(String inputPass,String saltDB){String formPass =inputPassToFormPass(inputPass);String dbPass =formPassToDBPass(formPass, saltDB);return dbPass;}publicstaticvoidmain(String[] args){System.out.println("对用户输入密码进行第1次加密:"+inputPassToFormPass("123456"));System.out.println("对用户输入密码进行第2次加密:"+formPassToDBPass(inputPassToFormPass("123456"),"1b2i3t4e"));System.out.println("对用户输入密码进行第2次加密:"+inputPassToDbPass("123456","1b2i3t4e"));}}

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

6.3.2 BCrypt 加密

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

添加依赖:

<!-- 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>

在 SpringBoot 启动了添加:

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

测试类:

importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: cm
 * Date: 2022-07-26
 * Time: 23:06
 */publicclassBCryptTest{publicstaticvoidmain(String[] args){//模拟从前端获得的密码String password ="123456";BCryptPasswordEncoder bCryptPasswordEncoder =newBCryptPasswordEncoder();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方法:参数一,待检验的未加密的密码 。参数二:从数据库中查询出的加密后密码 。

6.3.3 总结

  1. 密码学的应用安全,是建立在破解所要付出的成本远超出能得到的利益上的 。
  2. 使用BCrypt相比于MD5加密更好的一点在于,破解的难度上加大
  3. BCrypt的破解成本增加了,导致系统的运行成本也会大大的增加 。
  4. 回到本质的问题,你的数据库中的数据价值如何?如果你是银行类型的,那么使用BCrypt是不错的,一般情况使用MD5加盐,已经够用了。

参考链接:https://blog.csdn.net/muyimo/article/details/118811514

BCrypt加密: 一种加盐的单向Hash,不可逆的加密算法,同一种明文(plaintext),每次加密后的密文都不一样,而且不可反向破解生成明文,破解难度很大。
MD5加密: 是不加盐的单向Hash,不可逆的加密算法,同一个密码经过hash的时候生成的是同一个hash值,在大多数的情况下,有些经过md5加密的方法将会被破解。

Bcrypt生成的密文是60位的。而MD5的是32位的。

目前,MD5和BCrypt比较流行。相对来说,BCrypt比MD5更安全,但加密更慢。 虽然BCrpyt也是输入的字符串+盐,但是与MD5+盐的主要区别是:每次加的盐不同,导致每次生成的结果也不相同。无法比对!

6.4 注入 BCryptPasswordEncoder 对象

新建 AppConfig 类

@ConfigurationpublicclassAppConfigimplementsWebMvcConfigurer{@BeanpublicBCryptPasswordEncodergetBCryptPasswordEncoder(){returnnewBCryptPasswordEncoder();}}

6.5 拦截器

6.5.1 LoginInterceptor 类

publicclassLoginInterceptorimplementsHandlerInterceptor{@OverridepublicbooleanpreHandle(HttpServletRequest request,HttpServletResponse response,Object handler)throwsException{HttpSession httpSession = request.getSession(false);if(httpSession!=null&&
                httpSession.getAttribute(Constant.USERINFO_SESSION_KEY)!=null){//此时是登录状态returntrue;}returnfalse;}}

6.5.2 addInterceptors 方法

在 AppConfig 类中

/**
     * 添加拦截器,将自定义拦截器加入到系统配置
     * @param registry
     */@OverridepublicvoidaddInterceptors(InterceptorRegistry registry){//1、配置拦截规则LoginInterceptor loginInterceptor =newLoginInterceptor();
        registry.addInterceptor(loginInterceptor).addPathPatterns("/**")//排除所有的JS.excludePathPatterns("/js/**.js")//排除img下所有的元素.excludePathPatterns("/img/**").excludePathPatterns("/css/**.css").excludePathPatterns("/fronts/**").excludePathPatterns("/player/**").excludePathPatterns("/login.html").excludePathPatterns("/reg.html")//排除登录注册接口.excludePathPatterns("/user/login").excludePathPatterns("/user/reg");}

7. 登录实现

7.1 设计登录的请求和响应

请求
POST /user/login 

{username: "",password: ""}

响应
{
    status: 0/-1,
    message: "",
    data: ""
}

响应体设计字段解释:
{

status:

状态码,为0代表成功,负数代表失败

message:

状态描述信息,描述此次请求成功或者失败的原因

data:

返回的数据,请求成功后,需要给前端的数据信息
}

7.2 创建 User 类

我们这里登录需要进行数据库的 用户查询,根据用户名判断是否存在当前用户

注: 注解都不要忘记给加上

创建 model 包,底下创建 User 类

importlombok.Data;@DatapublicclassUser{privateInteger id;privateString username;privateString password;}

7.3 创建 Mapper

新建 mapper 包,新建 UserMapper

importcom.example.onlinemusic.model.User;importorg.apache.ibatis.annotations.Mapper;@MapperpublicinterfaceUserMapper{/**
     * 根据名字查询用户
     * @param username
     * @return
     */UserselectByName(String username);}

7.4 创建 UserMapper.xml

创建 mabatis 包,添加 xml 代码

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="com.example.onlinemusic.mapper.UserMapper"><!-- 根据用户名查找用户用户 --><selectid="selectByName"resultType="com.example.onlinemusic.model.User">
        select * from user where username=#{username}
    </select></mapper>

7.5 创建 Service

@ServicepublicclassUserService{@AutowiredprivateUserMapper userMapper;publicUserselectByName(String username){return userMapper.selectByName(username);}}

7.6 创建 Controller

@RestController@RequestMapping("/user")publicclassUserController{//注入@AutowiredprivateUserService userService;@Resource//在自动装配之前,需要完成注入,我们再AppConfig中进行注入privateBCryptPasswordEncoder bCryptPasswordEncoder;@RequestMapping("/login")publicResponseBodyMessage<User>login(@RequestParamString username,@RequestParamString password,HttpServletRequest request){// 查询用户是否在数据库中存在User user = userService.selectByName(username);// 没有查到if(user ==null){System.out.println("登录失败!");returnnewResponseBodyMessage<>(-1,"用户名或者密码错误",user);}else{//查到了,但密码不一样if(!bCryptPasswordEncoder.matches(password,user.getPassword())){returnnewResponseBodyMessage<>(-1,"用户名或者密码错误",user);}// 匹配成功,创建 session
            request.getSession().setAttribute(Constant.USERINFO_SESSION_KEY,user);returnnewResponseBodyMessage<>(0,"登录成功",user);}}}

7.7 测试

我用的是 postman 进行测试

在这里插入图片描述

7.8 前端代码

在这里插入图片描述

<script><!-- 核心业务逻辑 -->$(function(){$("#submit1").click(function(){var username =$("#username").val();var password =$("#password").val();
            console.log(username);
            console.log(password);if(username.trim()==""|| password.trim()==""){alert("用户名或密码不能为空!");return;}//执行异步HTTP(Ajax)请求
            $.ajax({url:"/user/login",data:{"username":username,"password":password},type:"POST",dataType:"json",success:function(data){
                    console.log(data);if(data.status ==0){alert("登录成功!");
                        window.location.href="list.html";}else{alert("登录失败,账号或者密码错误,请重新输入!");$("#message").text("账号或者密码错误,请重新输入!");$("#username").val("");$("#password").val("");}}});});});</script>

8. 注册实现

8.1 请求响应设计

请求
POST /user/reg

{username:"",password:""}

响应
{
    status:0/-1,
    message:"",
    data:""}

主要就是需要注意用户存在就不能进行注册

8.2 UserMapper 层

@MapperpublicinterfaceUserMapper{/**
     * 新增用户
     * @param user 
     * @return
     */intaddUser(User user);}

8.3 UserMapper.xml 层

<!-- 新增用户 --><insertid="addUser">
        insert into user(username,password) values (#{username},#{password});
    </insert>

8.4 UserService 层

publicintaddUser(User user){return userMapper.addUser(user);}

8.5 UserController 层

@RequestMapping("/reg")publicResponseBodyMessage<Boolean>reg(@RequestParamString username,@RequestParamString password){User user1 = userService.selectByName(username);if(user1 !=null){returnnewResponseBodyMessage<>(-1,"当前用户已存在!",false);}else{User user2 =newUser();
           user2.setUsername(username);String password1 = bCryptPasswordEncoder.encode(password);
           user2.setPassword(password1);
           userService.addUser(user2);returnnewResponseBodyMessage<>(0,"注册成功!",true);}}

8.6 测试

在这里插入图片描述
在这里插入图片描述

8.7 前端代码

在这里插入图片描述

<script>// 注册的数据提交 AJAXfunctionmySubmit(){// 1.对输入的内容进行非空和正确性效验var username =jQuery("#username");var password =jQuery("#password");var password2 =jQuery("#password2");if(jQuery.trim(username.val())==""){alert("抱歉:请先输入用户名!");
            username.focus();return;}if(jQuery.trim(password.val())==""){alert("抱歉:请先输入密码!");
            password.focus();return;}if(jQuery.trim(password2.val())==""){alert("抱歉:请先输入确认密码!");
            password2.focus();return;}// 密码强度效验(密码的长度必须大于 8)【扩展,密码组合强度效验】if(password.val().length<8){alert("抱歉:密码强度太低,密码位数必须大于等于8位!");
            password.focus();return;}if(password.val()!=password2.val()){alert("抱歉:两次输入的密码不一致,请检查!");
            password.focus();return;}// 2.发送 ajax 请求到后端
        jQuery.ajax({url:"user/reg",type:"POST",data:{"username":username.val(),"password":password.val()},dataTye:"json",success:function(data){if(data.status ==0){alert("注册成功!");
                    window.location.href="login.html";}else{alert("注册失败,当前用户已经存在!");
                    username.value="";
                    password.value="";
                    password2.value="";
                    username.focus();}}});}</script>

9. 上传音乐

9.1 请求响应设计

请求:
{
post,/music/upload
{singer,MultipartFile file},
} 

响应:
{"status":0,"message":"上传成功!","data":true}

9.2 新建 Music 类

@DatapublicclassMusic{privateInteger id;privateString title;privateString singer;privateString time;privateString url;privateInteger userid;}

9.3 MusicService 层

我们上传音乐需要注意的就是是否重复上传,我们这里是根据同一首歌是否是同一个歌手来判断的

@ServicepublicclassMusicService{@AutowiredprivateMusicMapper musicMapper;publicintinsert(String title,String singer,String time,String url,Integer userid){return musicMapper.insert(title,singer,time,url,userid);}publicList<Music>selectByTitle(String title){return musicMapper.selectByTitle(title);}}

9.4 MusicMapper 层

@MapperpublicinterfaceMusicMapper{/**
     * 插入音乐
     * @param title 音乐名
     * @param singer 歌手名
     * @param time 上传时间
     * @param url 对应地址
     * @param userid 上传的用户 id
     * @return 返回受影响的行数
     */publicintinsert(String title,String singer,String time,String url,Integer userid);/**
     * 通过音乐名去查找歌曲.
     * @param title 音乐名
     * @return 对应音乐名的所有歌曲
     */List<Music>selectByTitle(String title);}

9.5 MusicMapper.xml 层

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="com.example.onlinemusic.mapper.MusicMapper"><!-- 插入音乐 --><insertid="insert">
        insert into music(title,singer,time,url,userid)
        value(#{title},#{singer},#{time},#{url},#{userid})
    </insert><!-- 根据歌名查找音乐 --><selectid="selectByTitle"resultType="com.example.onlinemusic.model.Music">
        select * from music where title = #{title}
    </select></mapper>

9.6 MusicController 层

@RestController@RequestMapping("/music")publicclassMusicController{@ResourceprivateLoveMusicService loveMusicService;@ResourceprivateMusicService musicService;@Value("${music.local.path}")privateString SAVE_PATH;@RequestMapping("/upload")publicResponseBodyMessage<Boolean>insertMusic1(@RequestParamString singer,@RequestParam("filename")MultipartFile file,HttpServletRequest request,HttpServletResponse response){//1. 检查是否登录了HttpSession session = request.getSession(false);if(session ==null|| session.getAttribute(Constant.USERINFO_SESSION_KEY)==null){System.out.println("没有登录");returnnewResponseBodyMessage<>(-1,"请登录后上传",false);}// 2. 上传到服务器String fileNameAndType = file.getOriginalFilename();System.out.println("fileNameAndType: "+fileNameAndType);//检验同名歌曲不可上传String title = fileNameAndType.substring(0,fileNameAndType.lastIndexOf('.'));List<Music> list = musicService.selectByTitle(title);if(list !=null){for(Music music : list){if(music.getSinger().equals(singer)){returnnewResponseBodyMessage<>(-1,"当前歌手歌曲已经存在!",false);}}}// 创建文件String path = SAVE_PATH+fileNameAndType;File dest =newFile(path);if(!dest.exists()){
            dest.mkdir();}try{
            file.transferTo(dest);}catch(IOException e){
            e.printStackTrace();returnnewResponseBodyMessage<>(-1,"服务器上传失败!",false);}// 判断是不是 MP3 文件(TAG)File file1 =newFile(path);byte[] result =null;try{
            result =Files.readAllBytes(file1.toPath());if(result ==null){returnnewResponseBodyMessage<>(-1,"当前文件不存在!",false);}String str =newString(result);if(!str.contains("TAG")){
                file1.delete();returnnewResponseBodyMessage<>(-1,"当前上传的不是 MP3 文件!",false);}}catch(IOException e){
            e.printStackTrace();returnnewResponseBodyMessage<>(-1,"服务器上传失败2!",false);}// 进行数据库上传// 1. 准备数据 //2. 调用 insert//        int index = fileNameAndType.lastIndexOf(".");//最后一个点前面就是名字//        String title1 = fileNameAndType.substring(0,index);User user =(User)session.getAttribute(Constant.USERINFO_SESSION_KEY);int userid = user.getId();//1. 播放音乐 -> http 请求String url ="/music/get?path="+title;SimpleDateFormat sf =newSimpleDateFormat("yyyy-MM-dd");String time = sf.format(newDate());try{int ret =0;
            ret = musicService.insert(title, singer, time, url, userid);if(ret ==1){//应该跳转到音乐列表页面try{
                    response.sendRedirect("/list.html");}catch(IOException e){
                    e.printStackTrace();}returnnewResponseBodyMessage<>(0,"数据库上传成功",true);}else{returnnewResponseBodyMessage<>(-1,"数据库上传失败",false);}}catch(BindingException e){
            dest.delete();returnnewResponseBodyMessage<>(-1,"数据库上传失败",false);}}}

9.7 测试

在这里插入图片描述

9.8 前端代码

在这里插入图片描述

10. 播放音乐

10.1 请求响应设计

请求:
{
get,/music/get?path=xxx.mp3
} 

响应:
{
音乐数据本身的字节信息
}

10.2 MusicController 层

/**
     * 播放音乐的时候,/music/get?path=xxx.mp3
     *
     * @param path
     * @return
     */@RequestMapping("/get")publicResponseEntity<byte[]>get(String path){File file =newFile(SAVE_PATH +"/"+ path);byte[] a =newbyte[0];try{
            a =Files.readAllBytes(file.toPath());if(a.length ==0){returnResponseEntity.badRequest().build();}returnResponseEntity.ok(a);}catch(IOException e){
            e.printStackTrace();}//return ResponseEntity.internalServerError().build();//        return ResponseEntity.notFound().build();//        return ResponseEntity.ok(a);returnResponseEntity.badRequest().build();}

10.3 测试

在这里插入图片描述
有这个 TAG 就能判断是不是歌曲了

10.4 前端代码

 //播放
        function playerSong(obj) {
            console.log(obj)
            var name = obj.substring(obj.lastIndexOf('=')+1);
//obj:播放地址 name: 名字 0: 开始时间 true: 自动播放
            SewisePlayer.toPlay(obj,name,0,true);
        }
<scripttype="text/javascript"src="player/sewise.player.min.js"></script><scripttype="text/javascript">
        SewisePlayer.setup({server:"vod",type:"mp3",//这里是默认的一个网址videourl:"http://jackzhang1204.github.io/materials/where_did_time_go.mp3",skin:"vodWhite",//这里需要设置falseautostart:"false",});</script>

11. 删除音乐

11.1 删除单个音乐

11.1.1 请求响应设计

请求:
{
post,/music/delete,
id
} 

响应:
{"status":0,"message":"删除成功!","data":true}

11.1.2 MusicService 层

publicintdeleteById(int musicId){return musicMapper.deleteById(musicId);}

11.1.3 MusicMapper 层

/**
     * 删除对应音乐Id的歌曲
     * @param musicId 音乐Id
     * @return 返回影响行数
     */intdeleteById(int musicId);

11.1.4 MusicMapper.xml 层

<!-- 根据 id 删除音乐 --><deleteid="deleteById"parameterType="java.lang.Integer">
delete from music where id = #{id}
    </delete>

11.1.5 MusicController 层

/**
     * 删除单个音乐
     *
     * @param id
     * @return
     */@RequestMapping("/delete")publicResponseBodyMessage<Boolean>deleteMusicById(@RequestParamString id){//1. 先检查这个音乐是否存在int iid =Integer.parseInt(id);//2. 如果存在要进行删除Music music = musicService.findMusicById(iid);if(music ==null){System.out.println("没有这个 id 的音乐");returnnewResponseBodyMessage<>(-1,"没有你要删除的音乐",false);}else{//2.1 删除数据库int ret = musicService.deleteById(iid);if(ret ==1){//2.2删除服务器上的数据int index = music.getUrl().lastIndexOf("=");String fileName = music.getUrl().substring(index +1);File file =newFile(SAVE_PATH +"/"+ fileName +".mp3");System.out.println("当前的路径: "+ file.getPath());if(file.delete()){//需要同步删除lovemusic表当中的音乐
                    loveMusicService.deleteLoveMusicByMusicId(iid);returnnewResponseBodyMessage<>(0,"服务器当中的音乐删除成功!",true);}else{returnnewResponseBodyMessage<>(-1,"服务器当中的音乐删除失败!",false);}}else{returnnewResponseBodyMessage<>(-1,"数据库当中的音乐没有删除成功!",false);}}}

11.1.6 测试

在这里插入图片描述

11.1.7 前端代码

 //删除
        function deleteInfo(obj){
            console.log(obj);
            $.ajax({
                url:"/music/delete",
                type:"POST",
                data:{"id":obj},
                dataType: "json",
                success: function (obj){
                    console.log(obj);
                    if(obj.data===true){
                        alert("删除成功!");
                        window.location.href="list.html";
                    }else{
                        alert("删除失败!");
                    }
                }
            });
        }

11.2 批量删除选中音乐

11.2.1 请求响应设计

请求:
{
post,/music/deletesel,
data:{"id":id}} 

响应:
{"status":0,"message":"批量删除成功","data":true}

11.2.2 MusicController 层

/**
     * 批量进行音乐删除
     *
     * @param id
     * @return
     */@RequestMapping("/deletesel")publicResponseBodyMessage<Boolean>deleteSelMusic(@RequestParam("id[]")List<Integer> id){System.out.println("所有的ID: "+id);int sum =0;for(int i =0; i < id.size(); i++){int musicId = id.get(i);Music music = musicService.findMusicById(musicId);if(music ==null){System.out.println("没有这个 id 的音乐");returnnewResponseBodyMessage<>(-1,"没有你要删除的音乐",false);}int ret = musicService.deleteById(musicId);if(ret ==1){int index = music.getUrl().lastIndexOf("=");String fileName = music.getUrl().substring(index +1);File file =newFile(SAVE_PATH +"/"+ fileName +".mp3");if(file.delete()){//需要同步删除lovemusic表当中的音乐
                    loveMusicService.deleteLoveMusicByMusicId(musicId);
                    sum += ret;//            return new ResponseBodyMessage<>(0,"服务器当中的音乐删除成功!",true);}else{returnnewResponseBodyMessage<>(-1,"服务器当中的音乐删除失败!",false);}}else{returnnewResponseBodyMessage<>(0,"数据库当中音乐删除失败!",true);}}if(sum == id.size()){System.out.println("整体删除成功");returnnewResponseBodyMessage<>(0,"音乐删除成功!",true);}else{System.out.println("整体删除失败");returnnewResponseBodyMessage<>(0,"音乐删除失败!",true);}}

11.2.3 测试

在这里插入图片描述

11.2.4 前端代码

 $(function (){
            //新增函数
            $("#submit1").click(function () {
//获取exampleInputName2当中的值给name
                var name = $("#exampleInputName2").val();
                load(name);
            });
//当load这个函数执行成功,则执行done当中的回调函数
            $.when(load).done(function () {
//选取所有类型为CheckBox的元素
                $("#delete").click(function () {
                    var i=0;
                    var id=new Array();
//遍历checkbox
                    $("input:checkbox").each(function() {
//如果被选中,this代表发生事件的dom元素,<input>
                        if($(this).is(':checked')){
//获取id的值,存储到id数组当中
                            id[i] = $(this).attr("id");
                            i++;
                        }
                    });
                    console.log(id);
//发送ajax请求
                    $.ajax({
                        url:"/music/deletesel",
//将id数组,发送给deleteSelectedServlet
                        data:{"id":id},
                        type: "POST",
                        dataType:"json",
                        success:function (data) {
                            if(data.data===true){
                                alert("删除成功");
                                window.location.href="list.html";
                            }else{
                                alert("删除失败");
                            }
                        }
                    });
                });
            });
        });
    </script>

12. 查询音乐

12.1 请求响应设计

有两种:模糊查询和空查询

请求:
{
get,/music/findmusic,
data:{musicName:musicName},} 

响应:【不给musicName传参】
{"status":0/-1,"message":"","data":""} 

响应:【给musicName传参】
{"status":0/-1,"message":"","data":""}

12.2 MusicService 层

publicList<Music>findMusic(){return musicMapper.findMusic();}publicList<Music>findMusicByName(String musicName){return musicMapper.findMusicByName(musicName);}

12.3 MusicMapper 层

/**
     * 查询所有的音乐
     * @return
     */List<Music>findMusic();/**
     * 查询匹配音乐
     * @param musicName
     * @return
     */List<Music>findMusicByName(String musicName);

12.4 MusicMapper.xml 层

<!-- 查询所有音乐 --><selectid="findMusic"resultType="com.example.onlinemusic.model.Music">
select * from music
    </select><!-- 查询匹配音乐 --><selectid="findMusicByName"resultType="com.example.onlinemusic.model.Music">
        select * from music where title like concat('%',#{musicName},'%')
    </select>

12.5 MusicController 层

@RequestMapping("/findmusic")//(required=false)可以不传入参数publicResponseBodyMessage<List<Music>>findMusic(@RequestParam(required =false)String musicName){List<Music> musicList =null;if(musicName !=null){
            musicList = musicService.findMusicByName(musicName);}else{
            musicList = musicService.findMusic();}returnnewResponseBodyMessage<>(0,"查询到了所有的音乐",musicList);}

12.6 测试

在这里插入图片描述

在这里插入图片描述

12.7 前端代码

<scripttype="text/javascript"><!-- 核心代码实现 -->
        $(function (){
            load();
        });

        // musicName 可以传参,也可以不传参
        function load(musicName){
            $.ajax({
                url: "/music/findmusic",
                data:{musicName:musicName},
                type: "GET",
                dataType:"json",
                success:function (obj){
                    console.log(obj);
                    var data=obj.data;
                    console.log(data.length);
                    var s = '';
                    for(var i = 0;i < data.length;i++){
                        var musicUrl = data[i].url+".mp3";
                        s += '<tr>';
                        s += '<th><inputid="'+data[i].id+'"type="checkbox"></th>';
                        s += '<td>' + data[i].title + '</td>';
                        s += '<td>' + data[i].singer + '</td>';
                        s+='<td><buttonclass="btn btn-primary"onclick="playerSong(\''+musicUrl+'\')">播放歌曲</button>' +
                            '</td>';
                        s+='<td><buttonclass="btn btn-primary"onclick="deleteInfo('+ data[i].id + ')">删除</button>' +'  '+
                            '<buttonclass="btn btn-primary"onclick="loveInfo('+ data[i].id + ')"> 喜欢</button>'+
                            '</td>';
                        s += '</tr>';
                    }
                    $("#info").html(s);//把拼接好的页面添加到info的id下
                }
            });
        }

13. 收藏音乐

13.1 请求响应设计

请求:
{
post,/lovemusic/likemusic
data: id//音乐id} 

响应:
{"status":0,"message":"点赞音乐成功","data":true}

需要看之前是否收藏过

13.2 LoveMusicService 层

@ServicepublicclassLoveMusicService{@AutowiredprivateLoveMusicMapper loveMusicMapper;/**
     * 收藏音乐
     * @param userId
     * @param musicId
     * @return
     */publicbooleaninsertLoveMusic(Integer userId,Integer musicId){return loveMusicMapper.insertLoveMusic(userId, musicId);}/**
     * 查询喜欢的音乐
     * @param userId
     * @param musicId
     * @return
     */publicMusicfindLoveMusicByMusicIdAndUserId(Integer userId,Integer musicId){return loveMusicMapper.findLoveMusicByMusicIdAndUserId(userId,musicId);}}

13.3 LoveMusicMapper 层

@MapperpublicinterfaceLoveMusicMapper{/**
     * 收藏音乐
     * @param userId
     * @param musicId
     * @return
     */booleaninsertLoveMusic(Integer userId,Integer musicId);/**
     * 查询喜欢的音乐
     * @param userId
     * @param musicId
     * @return
     */MusicfindLoveMusicByMusicIdAndUserId(Integer userId,Integer musicId);}

13.4 LoveMusicMapper.xml 层

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="com.example.onlinemusic.mapper.LoveMusicMapper"><insertid="insertLoveMusic">
insert into lovemusic(user_id,music_id) values(#{userId},#{musicId})
    </insert><selectid="findLoveMusicByMusicIdAndUserId"resultType="com.example.onlinemusic.model.Music">
        select * from lovemusic where user_id=#{userId} and music_id=#{musicId}
    </select></mapper>

13.5 LoveMusicController 层

@RestController@RequestMapping("/lovemusic")publicclassLoveMusicController{@ResourceprivateLoveMusicService loveMusicService;@RequestMapping("/likemusic")publicResponseBodyMessage<Boolean>likeMusic(@RequestParamString id,HttpServletRequest request){int musicId =Integer.parseInt(id);System.out.println("musicId: "+musicId);//1. 检查是否登录了HttpSession session = request.getSession(false);if(session ==null|| session.getAttribute(Constant.USERINFO_SESSION_KEY)==null){System.out.println("没有登录");returnnewResponseBodyMessage<>(-1,"请登录后上传",false);}User user =(User) session.getAttribute(Constant.USERINFO_SESSION_KEY);int userId = user.getId();System.out.println("userId: "+userId);Music music = loveMusicService.findLoveMusicByMusicIdAndUserId(userId,musicId);if(music !=null){//之前收藏过,不能在收藏 可以在设置一个取消收藏returnnewResponseBodyMessage<>(-1,"您之前收藏过该音乐",false);}boolean effect = loveMusicService.insertLoveMusic(userId,musicId);if(effect){returnnewResponseBodyMessage<>(0,"收藏成功",true);}else{returnnewResponseBodyMessage<>(-1,"收藏失败",false);}}}

13.6 测试

在这里插入图片描述
在这里插入图片描述

13.7 前端代码

function loveInfo(obj) {
            console.log(obj);
            $.ajax({
                url: "/lovemusic/likemusic",
                type: "post",
//发送给后端的数据
                data: {"id": obj},
                dataType: "json",
                success: function (data) {
                    if (data.data === true) {
                        alert("加入喜欢的列表成功");
                        window.location.href = "list.html";
                    } else {
                        alert("加入喜欢的列表失败或者已经存在该音乐");
                    }
                }
            });
        }

14. 查询喜欢的音乐

跟之前的主页查询一样

14.1 请求响应设计

请求:
{
get,/lovemusic/findlovemusic,
data:{musicName:musicName},} 

响应:【不给musicName传参】
{"status":0/-1,"message":"","data":""} 

响应:【给musicName传参】
{"status":0/-1,"message":"","data":""}

14.2 LoveMusicService 层

/**
     * 查询这个用户收藏过的所有音乐
     * @param userId
     * @return
     */publicList<Music>findLoveMusicByUserId(Integer userId){return loveMusicMapper.findLoveMusicByUserId(userId);}/**
     * 查询当前用户,指定为musicName的音乐,支持模糊查询
     * @param musicName
     * @param userId
     * @return
     */publicList<Music>findLoveMusicBykeyAndUID(String musicName,Integer userId){return loveMusicMapper.findLoveMusicBykeyAndUID(musicName, userId);}

14.3 LoveMusicMapper 层

/**
     * 查询这个用户收藏过的所有音乐
     * @param userId
     * @return
     */List<Music>findLoveMusicByUserId(Integer userId);/**
     * 查询当前用户,指定为musicName的音乐,支持模糊查询
     * @param musicName
     * @param userId
     * @return
     */List<Music>findLoveMusicBykeyAndUID(String musicName,Integer userId);

14.4 LoveMusicMapper.xml 层

<selectid="findLoveMusicByUserId"resultType="com.example.onlinemusic.model.Music">
      select m.* from lovemusic lm,music m where m.id = lm.music_id and lm.user_id=#{userInd}
    </select><selectid="findLoveMusicBykeyAndUID"resultType="com.example.onlinemusic.model.Music">
        select m.* from lovemusic lm,music m where m.id = lm.music_id and lm.user_id=#{userId}
           and title like concat('%',#{musicName},'%')
    </select>

14.5 LoveMusicController 层

@RequestMapping("/findlovemusic")publicResponseBodyMessage<List<Music>>findLoveMusic(@RequestParam(required =false)String musicName,HttpServletRequest request){//1. 检查是否登录了HttpSession session = request.getSession(false);if(session ==null|| session.getAttribute(Constant.USERINFO_SESSION_KEY)==null){System.out.println("没有登录");returnnewResponseBodyMessage<>(-1,"请登录后查找",null);}User user =(User) session.getAttribute(Constant.USERINFO_SESSION_KEY);int userId = user.getId();System.out.println("userId: "+userId);List<Music> musicList =null;if(musicName ==null){
            musicList = loveMusicService.findLoveMusicByUserId(userId);}else{
            musicList = loveMusicService.findLoveMusicBykeyAndUID(musicName,userId);}returnnewResponseBodyMessage<>(0,"查询到了所有歌曲信息:", musicList);}

14.6 测试

在这里插入图片描述

14.7 前端代码

<script><!-- 核心代码实现 -->
        $(function (){
            load();
        });

        // musicName 可以传参,也可以不传参
        function load(musicName){
            $.ajax({
                url: "/lovemusic/findlovemusic",
                data:{musicName:musicName},
                type: "GET",
                dataType:"json",
                success:function (obj){
                    console.log(obj);
                    var data=obj.data;
                    console.log(data.length);
                    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><buttonclass="btn btn-primary"onclick="playerSong(\''+musicUrl+'\')">播放歌曲</button>' +
                            '</td>';
                        s += '<td><buttonclass="btn btn-primary"onclick="deleteInfo('+data[i].id+')"> 移除 </button>'+'</td>';
                        s += '</tr>';
                    }
                    $("#info").html(s);//把拼接好的页面添加到info的id下
                }
            });
        }

15. 移除收藏的音乐

15.1 请求响应设计

请求:
{
post,/lovemusic/deletelovemusic,
data:{id:id}} 

响应:
{"status":0,"message":"取消收藏成功!","data":true}

15.2 LoveMusicService 层

/**
     * 移除某个用户喜欢的音乐
     * @param userId
     * @param musicId
     * @return
     */publicintdeleteLoveMusic(Integer userId,Integer musicId){return loveMusicMapper.deleteLoveMusic(userId, musicId);}

15.3 LoveMusicMapper 层

/**
     * 移除某个用户喜欢的音乐
     * @param userId
     * @param musicId
     * @return
     */intdeleteLoveMusic(Integer userId,Integer musicId);

15.4 LoveMusicMapper.xml 层

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

15.5 LoveMusicController 层

@RequestMapping("/deletelovemusic")publicResponseBodyMessage<Boolean>deleteLoveMusic(@RequestParamString id,HttpServletRequest request){int musicId =Integer.parseInt(id);//1. 检查是否登录了HttpSession session = request.getSession(false);if(session ==null|| session.getAttribute(Constant.USERINFO_SESSION_KEY)==null){System.out.println("没有登录");returnnewResponseBodyMessage<>(-1,"请登录后删除",null);}User user =(User) session.getAttribute(Constant.USERINFO_SESSION_KEY);int userId = user.getId();int ret = loveMusicService.deleteLoveMusic(userId,musicId);if(ret ==1){returnnewResponseBodyMessage<>(0,"取消收藏成功",true);}else{returnnewResponseBodyMessage<>(-1,"取消收藏失败",false);}}

15.6 测试

在这里插入图片描述

15.7 前端代码

//移除
        function deleteInfo(obj){
            console.log(obj);
            $.ajax({
                url:"/lovemusic/deletelovemusic",
                type:"POST",
                data:{"id":obj},
                dataType: "json",
                success: function (obj){
                    console.log(obj);
                    if(obj.data===true){
                        alert("移除成功!");
                        window.location.href="loveMusic.html";
                    }else{
                        alert("移除失败!");
                    }
                }
            });
        }

本文转载自: https://blog.csdn.net/chenbaifan/article/details/126282948
版权归原作者 粉色的志明 所有, 如有侵权,请联系我们删除。

“基于 SpringBoot + Mybatis 的个人在线音乐平台”的评论:

还没有评论