JavaWeb——在线音乐播放器

news2024/11/26 2:02:46

文章目录

  • 效果演示
  • 1. 创建SpringBoot项目
  • 2. 数据库设计
  • 3. 配置数据库和xml
  • 4. 登录模块设计
    • 4.1 创建User类
    • 4.2 创建对应的Mapper和Controller
  • 5. 实现登录
    • 5.1 登录的请求和响应设计
    • 5.2 请求实现
    • 5.3 响应实现
      • 5.31 设置统一的响应体类工具类
      • 5.32 创建常量工具类
      • 5.33 优化后完整代码
  • 6. 实现加密登录
    • 6.1 Bcrypt加密设计
    • 6.2 加密登录实现
      • 6.21 创建包config,新建AppConfig类
    • 6.3 加密登录完整实现
  • 7. 上传音乐模块设计
    • 7.1 上传音乐的请求和响应设计
    • 7.2 新建 music 实体类
    • 7.3 实现服务器上传
      • 创建 MusicController 类
    • 7.4 如何判断上传的文件是mp3
    • 7.5 实现数据库上传
      • 实现 MusicMapper
      • 实现 MusicMapper.xml
      • 进行 数据库上传
      • 完整代码
  • 8. 播放音乐模块设计
    • 8.1 实现 ResponseEntity 类
  • 9. 删除音乐模块设计
    • 9.1 删除单个音乐
    • 9.2 批量删除
  • 10. 查询音乐模块设计
    • 10.1 实现 MusicMapper
    • 10.2 实现 MusicMapper.xml
    • 10.3 实现 MusicController
  • 11. 收藏/喜欢音乐模块设计
    • 11.1 实现 LoveMusicMapper
    • 11.2 实现 LoveMusicMapper.xml
    • 11.3 实现 LoveMusicController
  • 12. 查询收藏/喜欢音乐模块设计
    • 12.1 实现 LoveMusicMapper
    • 12.2 实现 LoveMusicMapper.xml
    • 12.3 实现 LoveMusicController
  • 13. 取消音乐收藏模块设计
    • 13.1 实现 LoveMusicMapper
    • 13.2 实现 LoveMusicMapper.xml
    • 13.3 实现 LoveMusicController
    • 13.4 存在BUG分析
  • 14. 删除音乐完善
  • 15. 注册实现
  • 前端页面实现
  • 16. 实现 登录界面 login.html
  • 17. 实现上传音乐 upload.html
  • 18. 实现音乐列表页 list.html
  • 19. 实现播放歌曲
  • 20. 实现删除单个音乐
  • 21. 实现查询音乐功能
  • 22. 实现删除选中音乐功能
  • 23. 实现喜欢音乐列表页功能
  • 24. 实现收藏音乐列表功能
  • 25 实现注册功能
  • 26 配置拦截器
  • 项目部署

效果演示

在这里插入图片描述

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

在这里插入图片描述

1. 创建SpringBoot项目

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

2. 数据库设计

创建 user 表:

用户 id,用户名 username,用户密码 password

创建 music 表:

音乐 id,音乐名称 title,歌手名称 singer,时间 time,歌曲路径 url,对应上传音乐的用户 userid

创建 lovemusic 表(中间表):

音乐 id,对应的用户id user_id,对应的音乐id music_id

-- 数据库
drop database if exists `onlinemusic`;
create database if not exists `onlinemusic` character set utf8;

-- 使用数据库
use `onlinemusic`;

-- 创建 user表
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`username` varchar(20) NOT NULL,
`password` varchar(255) NOT NULL
);

-- 创建 music 表
DROP TABLE IF EXISTS `music`;
CREATE TABLE `music` (
`id` int PRIMARY KEY AUTO_INCREMENT,
`title` varchar(50) NOT NULL,
`singer` varchar(30) NOT NULL,
`time` varchar(13) NOT NULL,
`url` varchar(1000) NOT NULL,
`userid` int(11) NOT NULL
);

-- 创建 lovemusic
DROP TABLE IF EXISTS `lovemusic`;
CREATE TABLE `lovemusic` (
`id` int PRIMARY KEY AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`music_id` int(11) NOT NULL
);

3. 配置数据库和xml

打开application.properties配置如下信息:

#配置数据库
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/onlinemusic?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=你的密码
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver


#配置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

# 配置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

4. 登录模块设计

4.1 创建User类

在package com.example.onlinemusic.model包中创建User类

@Data
public class User {
    private int id;
    private String username;
    private String password;
}

4.2 创建对应的Mapper和Controller

1.新建mapper包,在mapper包下新建UserMapper

@Mapper
public interface UserMapper {
    User login(User loginUser);
}

在这里插入图片描述

2.在resource目录下,新建mybatis文件夹,新建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">


</mapper>

在这里插入图片描述

5. 实现登录

5.1 登录的请求和响应设计

在这里插入图片描述

5.2 请求实现

在这里插入图片描述

@RestController // 这个注解是 @Controller 和 @RequestBody 的组合注解
@RequestMapping("/user") // 一级路由
public class UserController {

    @Autowired
    private UserMapper userMapper; // 注入 UserMapper
                // 后面要用 UserMapper里面的方法,去进行数据查询

    // 登录功能
    @RequestMapping("/login")
    // 传递两个参数 一个是 username 一个是 password
    public void login(@RequestParam String username,
                      @RequestParam String password) { // @RequestParam 注解,可以H后端参数重命名,也可以制定传参

        // 拿到 userLogin 对象,设置 用户名和密码
        User userLogin = new User();
        userLogin.setUsername(username);
        userLogin.setPassword(password);
        // 调用 userMapper的 login 方法 在数据库中 进行查询,返回 User
        User user =  userMapper.login(userLogin);

        if(user != null ) {

            System.out.println("登录成功!");
        }else {

            System.out.println("登录失败!");
        }
    }
}

UserMapper 中:

在这里插入图片描述

@Mapper
public interface UserMapper {

    // 登录功能,查询操作
    User login(User userLogin);// login 方法,返回为 User 对象
}

UserMapper.xml中:

在这里插入图片描述

<!-- 登录操作,查询 username 和 password,后面查询那个,就目录就是谁的,比如这里查询是User-->
    <select id="login" resultType="com.example.onlinemusic.model.User">
        select * from user where username=#{username} and password=#{password};
    </select>

5.3 响应实现

5.31 设置统一的响应体类工具类

在这里插入图片描述

@Data
// 这是统一响应体工具类
public class ResponseBodyMessage <T> {

    private int status; // 状态码
    private String message; // 返回的信息[出错或者没出错的原因]
    private T data; // 返回给前端的数据,有可能是 boolean 类型,类型很多这里直接用 泛型

    // 构造方法
    public ResponseBodyMessage(int status, String message, T data) {
        this.status = status;
        this.message = message;
        this.data = data;
    }
}

5.32 创建常量工具类

在这里插入图片描述

5.33 优化后完整代码

加了响应体之后,登录成功,我们再存储一下 Session

完整代码如下:

@RestController // 这个注解是 @Controller 和 @RequestBody 的组合注解
@RequestMapping("/user") // 一级路由
public class UserController {

    @Autowired
    private UserMapper userMapper; // 注入 UserMapper
                // 后面要用 UserMapper里面的方法,去进行数据查询

    // 登录功能
    @RequestMapping("/login")
    // 传递两个参数 一个是 username 一个是 password
        // 加了统一响应体类之后 返回值 不在是 void ,而是 我们的 ResponseBodyMessage
    public ResponseBodyMessage<User> login(@RequestParam String username,
                                           @RequestParam String password,
                                           HttpServletRequest request) {
        // @RequestParam 注解,可以H后端参数重命名,也可以制定传参


        // 拿到 userLogin 对象,设置 用户名和密码
        User userLogin = new User();
        userLogin.setUsername(username);
        userLogin.setPassword(password);
        // 调用 userMapper的 login 方法 在数据库中 进行查询,返回 User
        User user =  userMapper.login(userLogin);

        if(user != null ) {
            System.out.println("登录成功!");
            // 存储 session
//            request.getSession().setAttribute("USERINFO_SESSION_KEY",user);
            // 做出优化后的代码,我们将 USERINFO_SESSION_KEY 写在我们的工具包中
            request.getSession().setAttribute(Constant.USERINFO_SESSION_KEY,user);
            /**
             * request.getSession.setAttribute()是获得当前会话的session
             * 然后再setAttribute到session里面去,有效范围是session而不是request。
             */
            // 返回响应体
            return new ResponseBodyMessage<>(0,"登录成功老铁!",userLogin);
        }else {
            System.out.println("登录失败!");
            // 登录失败,做出响应
                // 在这里规定 status -1 为失败 0为成功
            return new ResponseBodyMessage<>(-1,"登录失败老铁!",userLogin);
        }
    }
}

6. 实现加密登录

6.1 Bcrypt加密设计

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

添加如下依赖到 pom.xml:

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

在这里插入图片描述

为什么要加 spring-boot 启动类注解:

当启动类,没有加这个过滤的时候,我们发现不能进行登录。
这是因为在SpringBoot中,默认的Spring Security生效了的,此时的接口都是被保护的,我们需要通过验证才能正常的访问。此时通过上述配置,即可禁用默认的登录验证。

实质我们并没有用到 Security 这个框架,而是用到了里面其中的一个类,仅此而以

在这里插入图片描述

创建 BcryptTest 测试类:

public class BcryptTest {
    public static void main(String[] args) {
//模拟从前端获得的密码
        // String password = "123456";
        // 获取 BCrypt 对象
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();

        // encode 对我们输入的密码进行加密处理 得到新的密码
        String newPassword = bCryptPasswordEncoder.encode(password);

        System.out.println("加密的密码为: " + newPassword);

//使用matches方法进行密码的校验
        boolean same_password_result = bCryptPasswordEncoder.matches(password, newPassword);
//返回true
        System.out.println("加密的密码和正确密码对比结果: " + same_password_result);
        boolean other_password_result = bCryptPasswordEncoder.matches("987654", newPassword);
//返回false
        System.out.println("加密的密码和错误的密码对比结果: " + other_password_result);
    }
}

6.2 加密登录实现

逻辑如下:

1.根据用户名名称 查询 当前是否存在这样的用户[用户名:默认是唯一的]

2.取出当前用户的密码,进行匹配,查看密码是否是一样的,一样就登录成功

6.21 创建包config,新建AppConfig类

这里为什么要建一个 AppConfig 类,是为了方便我们后面的对象注入,比如我 我们要在 加密登录中 注入 BCryptPasswordEncoder,我们就需要先创建一个对象,通过 @Bean 注解,将它输入到 Spring 容器中

在这里插入图片描述

当然直接在 UserController 当中进行 实例化创建 BCryptPasswordEncoder 对象也是一样的

//@Configuration:表明当前类是一个配置类,被注解的类内部包含有一个或多个被@Bean注解的方法,用于构建
//bean定义,初始化Spring容器。
//@Bean注解:用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。产生这个Bean对象的方
//法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。
//SpringIOC 容器管理一个或者多个bean,这些bean都需要在@Configuration注解下进行创建,在一个方法上使用
//@Bean注解就表明这个方法需要交给Spring进行管理。
@Configuration
public class AppConfig {
    @Bean
    public BCryptPasswordEncoder getBCryptPasswordEncoder() {
        return  new BCryptPasswordEncoder();
    }
}

UserMapper 代码如下:

在这里插入图片描述
UserMapper.xml 内容如下:

在这里插入图片描述

6.3 加密登录完整实现

在这里插入图片描述

/**
     * 实现加密登录
     */


    @RequestMapping("/login")
    // 传递两个参数 一个是 username 一个是 password
    // 加了统一响应体类之后 返回值 不在是 void ,而是 我们的 ResponseBodyMessage
    public ResponseBodyMessage<User> login(@RequestParam String username,
                                           @RequestParam String password,
                                           HttpServletRequest request) {

//        User userLogin = new User();
//        userLogin.setUsername(username);
//        userLogin.setPassword(password);
//        User user =  userMapper.login(userLogin);

        // 1. 先去查询我们 用户,是否存在
        User user = userMapper.selectByName(username);
        if(user != null ) {
            // 2. 如果用户存在,找到当前用户的密码,通过 Bcrypt 中的 matches 方法 判断密码是否一致
            // password 为原来的密码,user.getPassword 为加密后的密码
            boolean flg = bCryptPasswordEncoder.matches(password,user.getPassword());
            if(!flg) {
                // 说明密码不匹配
                return new ResponseBodyMessage<>(-1,"登录失败,用户名或密码错误",user);
            }
            System.out.println("登录成功!");
            request.getSession().setAttribute(Constant.USERINFO_SESSION_KEY,user);
            return new ResponseBodyMessage<>(0,"登录成功老铁!",user);
        }else {
            System.out.println("登录失败!");
            return new ResponseBodyMessage<>(-1,"登录失败,没找到用户!",user);
        }
    }

7. 上传音乐模块设计

7.1 上传音乐的请求和响应设计

在这里插入图片描述

7.2 新建 music 实体类

@Data
public class Music {
    private int id;
    private String title;
    private String singer;
    private String time;
    private String url;
    private int userId;
}

7.3 实现服务器上传

创建 MusicController 类

@RestController // 组合注解 @Controller + @ResponseBody
@RequestMapping("/music")
public class MusicController {

    /**
     * 上传音乐
     * @return
     */

    // 从配置文件中 将 路径 读取出来
    @Value("${music.local.path}")
    private String SAVE_PATH;

    @RequestMapping("/upload")
    public ResponseBodyMessage<Boolean> insertMusic(@RequestParam String singer,
                                                    @RequestParam("filename") MultipartFile file,
                                                    HttpServletRequest request) {
        // 1. 检查是否登录
            // 获取 session
        HttpSession session = request.getSession(false);
            // 判空
        if(session == null || session.getAttribute
                (Constant.USERINFO_SESSION_KEY) == null) {
            System.out.println("没有登录!");
            return new ResponseBodyMessage<>(-1,"请登录后上传!",false);
        }

        // 2. 上传到了服务器 ---- 拿到完整的文件名称 xxx.mp3
        String fileNameAndType = file.getOriginalFilename(); // 获取完整文件名称

        System.out.println("fileNameAndType" + fileNameAndType);

        String path = SAVE_PATH + fileNameAndType;

        File dest = new File(path); // dest 目录

        if(!dest.exists()) {
            dest.mkdir(); // 如果 dest(目录)不存在 创建目录
        }

        // 如果存在,通过 file.transferTo 将文件上传
        try {
            file.transferTo(dest);
            return new ResponseBodyMessage<>(0,"上传成功!",true);
        } catch (IOException e) {
            e.printStackTrace();
        }
            return new ResponseBodyMessage<>(-1,"上传失败!",false);
        
    }

}

7.4 如何判断上传的文件是mp3

每个种类的文件都要自己的格式,检测当前你上传的格式,判断这个文件的格式是不是 mp3 文件

不能通过后缀名进行判断,后缀名可以进行更改的

在这里插入图片描述

7.5 实现数据库上传

实现 MusicMapper

在这里插入图片描述

实现 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 所对应的目录-->
<mapper namespace="com.example.onlinemusic.mapper.MusicMapper">

    
        <insert id="insert" >
            insert into music(title,singer,time,url,userid)
            values(#{title},#{singer},#{time},#{url},#{userid});
        </insert>

</mapper>

进行 数据库上传

在这里插入图片描述
分别获取需要的数据:

tilte,singer,userid,url,time

/**
         * 进行 数据库 上传
         */
        // 1. 先准备需要数据

            // 1.1 获取title xxx.mp3 -- > title = xxx
        int index = fileNameAndType.lastIndexOf(".");
        String title = fileNameAndType.substring(0,index); // 拿到 xxx

            // 1.2 获取 singer 已经确定,我们传参已经传了

            // 1.3 获取 userid
        User user = (User) session.getAttribute(Constant.USERINFO_SESSION_KEY);
        int userid = user.getId();

            // 1.4 获取 url,播放音乐-》http请求
        String url = "/music/get?path="+title;

            //  1.5 获取 年-月-日
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
            // 把当前的日期格式化为 time format()格式化
        String time = sf.format(new Date());

        // 2. 调用 insert
        int ret = 0; // 插入影响的是行数,这里我们初始化一下,看看他等不等于1,等于1说明插入成功
        ret = musicMapper.insert(title,singer,time,url,userid);
        if(ret ==1 ) {
            return new ResponseBodyMessage<>(1,"数据库上传成功",true);
        }else {
            return new ResponseBodyMessage<>(-1,"数据库上传失败",false);
        }

    }

完整代码

@RestController // 组合注解 @Controller + @ResponseBody
@RequestMapping("/music")
public class MusicController {

    /**
     * 上传音乐
     * @return
     */

    @Autowired
    private MusicMapper musicMapper;
    // 从配置文件中 将 路径 读取出来
    @Value("${music.local.path}")
    private String SAVE_PATH;

    @RequestMapping("/upload")
    public ResponseBodyMessage<Boolean> insertMusic(@RequestParam String singer,
                                                    @RequestParam("filename") MultipartFile file,
                                                    HttpServletRequest request) {
        // 1. 检查是否登录
            // 获取 session
        HttpSession session = request.getSession(false);
            // 判空
        if(session == null || session.getAttribute
                (Constant.USERINFO_SESSION_KEY) == null) {
            System.out.println("没有登录!");
            return new ResponseBodyMessage<>(-1,"请登录后上传!",false);
        }

        // 先查询数据库当中是否有当前音乐[歌曲名 + 歌手]
        // TODD:


        // 2. 上传到了服务器 ---- 拿到完整的文件名称 xxx.mp3
        String fileNameAndType = file.getOriginalFilename(); // 获取完整文件名称

        System.out.println("fileNameAndType" + fileNameAndType);

        String path = SAVE_PATH + fileNameAndType;

        File dest = new File(path); // dest 目录

        if(!dest.exists()) {
            dest.mkdir(); // 如果 dest(目录)不存在 创建目录
        }

        // 如果存在,通过 file.transferTo 将文件上传
        try {
            file.transferTo(dest);
        } catch (IOException e) {
            e.printStackTrace();
            return new ResponseBodyMessage<>(-1,"服务器上传失败!",false);
        }

        /**
         * 进行 数据库 上传
         */
        // 1. 先准备需要数据

            // 1.1 获取title xxx.mp3 -- > title = xxx
        int index = fileNameAndType.lastIndexOf(".");
        String title = fileNameAndType.substring(0,index); // 拿到 xxx

            // 1.2 获取 singer 已经确定,我们传参已经传了

            // 1.3 获取 userid
        User user = (User) session.getAttribute(Constant.USERINFO_SESSION_KEY);
        int userid = user.getId();

            // 1.4 获取 url,播放音乐-》http请求
        String url = "/music/get?path="+title;

            //  1.5 获取 年-月-日
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
            // 把当前的日期格式化为 time format()格式化
        String time = sf.format(new Date());

        try {
            // 2. 调用 insert
            int ret = 0; // 插入影响的是行数,这里我们初始化一下,看看他等不等于1,等于1说明插入成功
            ret = musicMapper.insert(title,singer,time,url,userid);
            if(ret ==1 ) {
                return new ResponseBodyMessage<>(0,"数据库上传成功",true);
            }else {
                return new ResponseBodyMessage<>(-1,"数据库上传失败",false);
            }

        }catch (BindingException e) {
            dest.delete();
            return new ResponseBodyMessage<>(-1,"数据库上传失败",false);
        }

        // 另外一个问题: 如果重复上传一首歌曲 能否上传成功? 可以
    }

}

8. 播放音乐模块设计

在这里插入图片描述

8.1 实现 ResponseEntity 类

 /**
     * 播放音乐模块设计
     */

    // 播放音乐的时候,路径:/music/get?path=xxx.mp3
    @RequestMapping("/get")
    public ResponseEntity<byte[]> get(String path) {
        File file = new File(SAVE_PATH + path);
        byte[] a = null;
        try {
            a = Files.readAllBytes(file.toPath());
            //Files.readAllBytes(String path) :
            // 读取文件中的所有字节,读入内存 ,参数path是文件的路径

            if(a == null) {
                // 无参ok方法返回OK状态
                // 有参ok方法返回body内容和OK状态
                return ResponseEntity.badRequest().build();
            }
            return ResponseEntity.ok(a);
        } catch (IOException e) {
            e.printStackTrace();
        }
            return ResponseEntity.badRequest().build();
//        return ResponseEntity.internalServerError().build();
//        return ResponseEntity.notFound().build();
    }

9. 删除音乐模块设计

9.1 删除单个音乐

请求和响应设计:

data: 成功 or 失败

在这里插入图片描述
1.MusicMapper 内容如下:

在这里插入图片描述

2.MusicMapper.xml 内容如下
在这里插入图片描述

3.MusicController 内容如下:

在这里插入图片描述

@RequestMapping("/delete")
    public ResponseBodyMessage<Boolean> deleteMusicById(@RequestParam String id) {
        // 1. 先检查音乐是否存在
        int iid = Integer.parseInt(id);
        // 2. 如果存在进行删除
        Music music = musicMapper.findMusicById(iid);
        if(music == null) {
            return new ResponseBodyMessage<>(-1,"没有你要删除的音乐!",false);
        }else {
            // 2.1 删除数据库
            int ret = musicMapper.deleteMusicById(iid);
            // 如果 ret 等于1 说明 数据库数据删除成功
            if(ret == 1) {
                // 2.2 删除服务器上的数据(file)
                    // 这里需要拿到 title,可以通过 url 去拿,也可以直接 music.getTitle
                int index = music.getUrl().lastIndexOf("=");
                String fileName = music.getUrl().substring(index+1);
                File file = new File(SAVE_PATH + fileName + ".mp3");
                System.out.println("当前的路径: "  + file.getPath());

                if(file.delete()){
                    return  new ResponseBodyMessage<>(0,"服务器当中的音乐删除成功!",true);
                }else {
                    return  new ResponseBodyMessage<>(-1,"服务器当中的音乐删除失败!",false);
                }
            }else {
                return new ResponseBodyMessage<>(-1,"数据库当中的音乐没有删除成功",false);
            }

        }

    }

9.2 批量删除

约定前后端相互接口:

在这里插入图片描述

MusicController 内容如下:

/**
     * 批量删除
     * id[1,3,5,7,9]
     */
    @RequestMapping("/deleteSel")
    public ResponseBodyMessage<Boolean> deleteSelMusic(@RequestParam("id[]")
                                                               List<Integer> id) {
        System.out.println("所有的id:" + id);
        int sum = 0;
        // 因为是批量删除,这里用数组存储我们要删除的 音乐 id
        for (int i = 0; i < id.size(); i++) {
            // 1. 查询 音乐是否存在
            Music music = musicMapper.findMusicById(id.get(i));
            if (music == null) {
                System.out.println("没有这个id的音乐");
                return new ResponseBodyMessage<>(-1, "没有你要删除的音乐", false);
            }
            // 2. 如果音乐存在我们进行删除
            // 2.1 进行数据库删除
            int ret = musicMapper.deleteMusicById(id.get(i));
            if (ret == 1) {
                // 说明 数据库删除成功,进行服务器删除
                // 先拿到 title
                int index = music.getUrl().lastIndexOf("=");
                String fileName = music.getUrl().substring(index + 1);
                File file = new File(SAVE_PATH + fileName + ".mp3");
                System.out.println("当前路径:" + file.getPath());
                if (file.delete()) {
                    sum += ret;
                } else {
                    return new ResponseBodyMessage<>(-1, "服务器当中的音乐删除失败!", false);
                }
            }else {
                return new ResponseBodyMessage<>(-1,"数据库当中音乐删除失败",false);
            }
        }
        // 判断 sum 的值是否等于 id.size 如果等于说明 删完啦
        if(sum == id.size()) {
            System.out.println("整体删除成功!");
            return new ResponseBodyMessage<>(0,"音乐删除成功",true);
        }else {
            System.out.println("整体删除失败!");
            return new ResponseBodyMessage<>(-1,"音乐删除失败",false);
        }
    }

10. 查询音乐模块设计

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

  1. 支持模糊查询
  2. 支持传入参数为空

请求和响应设计:

在这里插入图片描述

10.1 实现 MusicMapper

在这里插入图片描述

10.2 实现 MusicMapper.xml

模糊查询:

select * from music where title like concat('%',#{musicName},'%');

在这里插入图片描述

10.3 实现 MusicController

 @RequestMapping("/findmusic")
    // 这里有一个小细节,我们可能不传参那么 我们的 @RequestParam 这里 需要 required = false
    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<>(0,"查询到了所有的音乐",musicList);
    }

11. 收藏/喜欢音乐模块设计

请求和响应设计:

在这里插入图片描述

11.1 实现 LoveMusicMapper

在这里插入图片描述

11.2 实现 LoveMusicMapper.xml

在这里插入图片描述

11.3 实现 LoveMusicController

/**
     * 收藏音乐,需要获取到 userId 和 musicId,musicId 传入的参数,userId 通过 session 获取
     */
    @RequestMapping("/likeMusic")
    public ResponseBodyMessage<Boolean> likeMusic(@RequestParam String id, HttpServletRequest request) {

        // 字符串变为整数
        int musicId = Integer.parseInt(id);
        System.out.println("musicId:" + musicId);
        // 1. 检查是否登录
        HttpSession session = request.getSession(false); // 获取 session
        // 判断 session 是否为空
        if(session == null || session.getAttribute(Constant.USERINFO_SESSION_KEY) == null) {
            System.out.println("没有登录!");
            return new ResponseBodyMessage<>(-1,"请先登录!",false);
        }
        // 如果不为空 获取 session 信息
        User user = (User) session.getAttribute(Constant.USERINFO_SESSION_KEY);
        int userId = user.getId();
        System.out.println("userId:" + userId);

        Music music = loveMusicMapper.findLoveMusic(userId,musicId);

        if(music != null) {
            // 如果不等于null,说明之前收藏过当前的音乐,不能进行收藏 TODD:加一个取消收藏的音乐
            return new ResponseBodyMessage<>(-1,"之前收藏过这个音乐",false);
        }
        // 如果等于空,收藏这首音乐 (插入)
        boolean effect = loveMusicMapper.insertLoveMusic(userId,musicId);
        if(effect) {
            return new ResponseBodyMessage<>(0,"收藏成功",true);
        }else {
            return new ResponseBodyMessage<>(-1,"收藏失败",false);
        }

    }

12. 查询收藏/喜欢音乐模块设计

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

  1. 支持模糊查询
  2. 支持传入参数为空

请求和响应设计,和查询音乐模块是一样的,这里不在做过多的阐述

12.1 实现 LoveMusicMapper

在这里插入图片描述

12.2 实现 LoveMusicMapper.xml

支持模糊查询,多表联合查询

在这里插入图片描述

12.3 实现 LoveMusicController

 @RequestMapping("/findlovermusic")
    public ResponseBodyMessage<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("没有登录");
            return new ResponseBodyMessage<>(-1,"没有登录",null);
        }
        // 如果不为空,进行查询
        // 先获取 userId
        User user = (User) session.getAttribute(Constant.USERINFO_SESSION_KEY);
        int userId = user.getId();
        List<Music> musicList = null;
        if(musicName == null) {
            // 如果等于空,查询所有的音乐
             musicList = loveMusicMapper.findLoveMusicByUserId(userId);

        }else {
            // 如果不等于空,查询 指定收藏的音乐
             musicList = loveMusicMapper.findLoveMusicByKeyAndUid(musicName,userId);
        }
        return new ResponseBodyMessage<>(0,"查询到了所有的歌曲信息",musicList);
    }

13. 取消音乐收藏模块设计

请求和响应设计

在这里插入图片描述

13.1 实现 LoveMusicMapper

在这里插入图片描述

13.2 实现 LoveMusicMapper.xml

在这里插入图片描述

13.3 实现 LoveMusicController

 @RequestMapping("/deletelovemusic")
    public ResponseBodyMessage<Boolean> deleteLoveMusic(@RequestParam String id,
                                                        HttpServletRequest request) {

        // 将 id 转为 整数
        int musicId = Integer.parseInt(id);

        // 拿到 useId,先检查是否 登录
        HttpSession session = request.getSession(false);
        if(session == null || session.getAttribute(Constant.USERINFO_SESSION_KEY) == null) {
            System.out.println("没有登录");
            return new ResponseBodyMessage<>(-1,"没有登录",false);
        }
        // 如果 session 不等于 空 拿到  use 信息
        User user = (User) session.getAttribute(Constant.USERINFO_SESSION_KEY);
        int userId = user.getId();

        // 获取到 userId 和 musicId 后调用 mapper 中的方法 去删除 ,首先判断是否存在
        int ret = loveMusicMapper.deleteLoveMusic(userId,musicId);
        if(ret == 1) {
            System.out.println("取消收藏成功!");
            return new ResponseBodyMessage<>(0,"取消收藏成功",true);

        }else {
            System.out.println("取消收藏失败");
            return new ResponseBodyMessage<>(-1,"取消收藏失败",false);
        }
    }

13.4 存在BUG分析

当删除 music 表 当中的 musicId 为 5的这首音乐的时候,请问 lovemusic这张表中,是不是应该也被删除?

music 表

在这里插入图片描述

lovemusic表
在这里插入图片描述

14. 删除音乐完善

我们在删除 音乐模块中,进行删除,需要同步删除我们 喜欢的音乐(lovemuisc),因此,我们对 MusicController 中进行优化和完善。

在 MusicController 中,删除 单个音乐中 添加以下内容:

在这里插入图片描述

在 批量删除中 添加以下内容:

在这里插入图片描述

15. 注册实现

在这里插入图片描述

@RequestMapping("/logon")
    public ResponseBodyMessage<User> logon(@RequestParam String username,@RequestParam String password,
                                              HttpServletRequest request) {
        // 1. 先对 输入的 password 进行加密
        String newPassword = bCryptPasswordEncoder.encode(password);

        // 创建 User 对象
        User user = new User();
        // 设置账号和 账号 和 密码
        user.setUsername(username);
        user.setPassword(newPassword);
        // 在数据库中进行查询 看用户是否存在
        User user1 = userMapper.selectByName(username);
        if(user1 != null) {
            System.out.println("用户已注册");
            return new ResponseBodyMessage<>(-1,"用户已注册,换个其他的吧",user);
        }
            // 注册,往数据库中插入 新的 用户
            int ret = userMapper.insertUser(user);
            if(ret == 1) {
                System.out.println("注册成功啦,我的老baby,一起来听音乐吧!");
                return new ResponseBodyMessage<>(0,"注册成功啦,我的老baby,一起来听音乐吧!",user);
            }else {
                System.out.println("注册失败!达咩~");
                return new ResponseBodyMessage<>(-1,"注册失败!达咩~",user);
            }
    }

以上,后端逻辑全部完善~~~~,接下来是后端模块实现。


前端页面实现

将前端页面模板,导入到我们 resources 底下的 static 中

在这里插入图片描述

jquery参考手册

16. 实现 登录界面 login.html

JS核心代码如下:

	  
<script>
	//    1. 登录核心业务逻辑 
	//原本是$(document).ready(function(){}) 表示当这个页面的dom树加载完成后才会执行这个,而后面的(document).ready可以省略,因此这里就是这样写也是可以的
	
	$(function(){
		//此时设置提交按钮的click事件,通过id选择器来获取
		$("#submit").click(function(){
			//此时就需要获取到用户名和密码的值
			let username = $("#user").val();
			let 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){
					//看状态码
					if(data.status == 0){
						console.log(data);
						alert("登录成功,点击进行跳转!");
						//登录成功,这里就可以进行页面的跳转
						window.location.href="list.html";
					}else{
						alert("登录失败,用户名或密码错误!");
						$("#password").val("");
					}

				}

			});

		});

	});
	  
  </script>

17. 实现上传音乐 upload.html

  1. 修改后端代码,上传音乐成功后,跳转到 音乐 列表页
    在这里插入图片描述
    在这里插入图片描述
  2. 前端代码 upload.html 实现:
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>

</head>
<body>
<!--enctype="multipart/form-data" action 提交 url 地址-->
<form method="POST" enctype="multipart/form-data" action="/music/upload"> 
    文件上传:<input type="file" name="filename"/>
    歌手名: <label>
    <input type="text" name="singer" placeholder="请输入歌手名"/>
    </label>
    <input type="submit" value="上传"/>
</form>
</body>
</html>

18. 实现音乐列表页 list.html

当我们跳转到 list.html 以后,我们要像服务器发起请求,查询到所有的音乐信息,动态的生成表格

在这里插入图片描述
之前我们前面说到,当我们 查询音乐的时候,如果不传参数,那么查询到所有的音乐,传递参数,查询到指定的音乐

JS代码如下:

script type="text/javascript">
        // 核心代码实现


        // 1. 上传音乐
        
         // <!-- 核心代码实现 -->
         $(function(){
            load();
        });

        // musicName 可以 默认传参 和 不传参
        // 不传参 匹配的就是所有的 音乐
        function load(musicName) {

            $.ajax({
                type:"GET",
				url:"/music/findmusic",
				//数据
				data:{"musicName":musicName},
				//服务器返回数据类型
				dataType:"json",
                // 如果服务器返回成功,会返回我们的回调函数
                
                success:function(obj) {
                    console.log(obj);
                    // obj包含了所有的返回信息,然后其中的data就包含了所有的音乐信息,可以先获取到
                    var data = obj.data;

                    var s = ''; // 最原始的拼接方式
                    // data[i].id  data[i].singer data[i].title 这种形式来获取 obj里面的信息
                    for(var i = 0; i < data.length;i++) {
                        
                        var musicUrl = data[i].url + ".mp3";
                       
                        s += '<tr>';
                 
                            s += '<th> <input id= "' + data[i].id + '" type="checkbox"> </th>';// <th> <input id="1" 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=\"none\" loop=\"loop\">  >"  + "</audio> </a> </td>";
                            s += '<td> <button class="btn btn-primary" onclick="playerSong(\''+musicUrl+'\')"> 播放歌曲 </button>' + '</td>';
                            s += '<td> <button class="btn btn-primary" onclick="deleteInfo('+data[i].id+')"> 删除 </button> <button class="btn btn-primary" onclick="loveInfo('+data[i].id+')"> 喜欢 </button>' + '</td>';

                        s += '</tr>';
                    }
                    //然后将所有的数据放到tbody里面
                    $('#info').html(s);
                }
            });
        }


		
    </script>

19. 实现播放歌曲

这里播放歌曲,我们采用开源的播放控件:

码云地址

GitHub

将该开源项目,下载到本地,取出player文件夹,放入static文件夹下

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
JS 核心代码,就是调用 播放器的 toPlay() 方法

toPlay(url,title,startTime,autoPlay) 这里的 autoPlay 设置为 true 代表 我们点击播放的时候,它才会播放

 //  实现音乐播放
        function playerSong(obj) {
            // toPlay(url,title,startTime,autoPlay)
            //obj: http://localhost:8080/music/get?path=xxx.mp3
            // 从等号下标 下一个位置开始截取 [)
            var title = obj.substring(obj.lastIndexOf("=") + 1);
            SewisePlayer.toPlay(obj,title,0,true);

        }

20. 实现删除单个音乐

之前约定好的 前后端交互接口

前端传入的数据和后端返回的响应,响应的 data 为 true or false
在这里插入图片描述

JS 核心代码如下:

// 删除音乐 传入的参数是 id
        function deleteInfo(obj){
            console.log(obj);
            $.ajax({
                type:"POST",
                url:"/music/delete",
                data:{"id":obj},
                dataType:"json",
                success:function(body){
                    console.log(body);
                    //判断是否删除成功
                    if(body.data == true){
                        //表示删除成功
                        alert("删除成功了老铁!,重新加载当前页面哈!");
                        window.location.href="list.html";

                    }else{
                        alert("删除失败了老铁!");
                    }

                }
            });
        }

21. 实现查询音乐功能

思路很简单,我们前面实现音乐列表页的时候 完成了 load 查询函数,这里我们只需要点击 按钮,执行我们的回调函数,拿到我们的 输入框的信息,即可,进行查询

在这里插入图片描述

JS核心代码如下:

 // 查询和删除
            // 查询~~~~~~
        $(function(){
            // 点击 提交按钮执行 回调函数
            $("#submit1").click( function(){
                //这里的功能就是进行查询而框里面不传参数就是默认的查询所有音乐
                //传了参数就进行模糊查询
                var name = $("#exampleInputName2").val();
                load(name);
            });
         });

22. 实现删除选中音乐功能

这里的删除,我们的删除逻辑为,点击删除,如图:

在这里插入图片描述

在这里插入图片描述

我们需要获取到 每一行 中的 input 标签中的 checkbox

首先获取到我们需要删除的音乐id并存储起来,其次再通过ajax和后端建立联系,进行删除。

JS 核心代码如下:

// 删除音乐
            /**
             *  1. 先拿到 需要删除的 id
             *  拿到 需要删除的 id 就存储起来
             * */
            // when 当执行完 load函数,则执行 done 当中的回调函数
            $.when(load).done(function() {
                //删除选中的回调事件
                $("#delete").click(function(){
                    //由于这里存储的不止一条数据,因此这里需要将这些选中的存储下来
                    var id = new Array();
                    var i = 0; 
                    //然后遍历所有的checkbox标签
                    $("input:checkbox").each(function(){
                        //看有没有被选中,被选中了就记录下来,没有选中就不用管
                        //this 发生事件的 demo元素,checked表示看是否选中了
                        if($(this).is(":checked")){
                            id[i] = $(this).attr("id");
                            i++;
                        }
                    });
                    console.log(id);


                    /**
                     * 2. 找到 id以后我们通过 ajax 给后端发送请求,传递给后端我们需要
                     * 删除的音乐
                     * */

                     $.ajax({
                        url:"/music/deleteSel",
                        type:"POST",
                        data:{"id":id},
                        dataType:"json",
                        // 执行成功 回调这个函数
                        success:function(obj){
                            // 看是否删除成功
                            if(obj.status == 0) {
                                alert("删除成功,重新加载此页面!");
                                window.location.href="list.html";
                            }else{
                                alert("删除失败!");
                            }
                        }

                     });

23. 实现喜欢音乐列表页功能

收藏音乐列表页,和 list.html 基本一样,但是需要注意我们需要将ajax中的 url 进行修改:

列入如下:
在这里插入图片描述

loveMusci.html 如下:

<script type="text/javascript">
    
        function load(musicName) {

        $.ajax({
        type:"GET",
        url:"/lovemusic/findlovermusic",
        //数据
        data:{"musicName":musicName},
        //服务器返回数据类型
        dataType:"json",
        // 如果服务器返回成功,会返回我们的回调函数
    
        success:function(obj) {
        console.log(obj);
        // obj包含了所有的返回信息,然后其中的data就包含了所有的音乐信息,可以先获取到
        var data = obj.data;

        var s = ''; // 最原始的拼接方式
        // data[i].id  data[i].singer data[i].title 这种形式来获取 obj里面的信息
        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> <button class="btn btn-primary" onclick="playerSong(\''+musicUrl+'\')"> 播放歌曲 </button>' + '</td>';
                s += '<td> <button class="btn btn-primary" onclick="deleteInfo('+data[i].id+')"> 移除 </button>'  + '</td>';

            s += '</tr>';
               }
                    //然后将所有的数据放到tbody里面
                $('#info').html(s);
            }
         });
    }
        
        //  实现音乐播放
        function playerSong(obj) {
            // toPlay(url,title,startTime,autoPlay)
            //obj: http://localhost:8080/music/get?path=xxx.mp3
            // 从等号下标 下一个位置开始截取 [)
            var title = obj.substring(obj.lastIndexOf("=") + 1);
            
            SewisePlayer.toPlay(obj,title,0,true);

        }

        // 删除喜欢音乐 传入的参数是 id
        function deleteInfo(obj){
            console.log(obj);
            $.ajax({
                type:"POST",
                url:"/lovemusic/deletelovemusic",
                data:{"id":obj},
                dataType:"json",
                success:function(body){
                    console.log(body);
                    //判断是否删除成功
                    if(body.data == true){
                        //表示删除成功
                        alert("删除成功了老铁!,重新加载当前页面哈!");
                        window.location.href="list.html";

                    }else{
                        alert("删除失败了老铁!");
                    }

                }
            });
        }




		
    </script>

24. 实现收藏音乐列表功能

在 list.html 中 实现如下核心代码:

   // 添加喜欢的音乐/收藏音乐
        function loveInfo(obj){
            //直接发送请求
            $.ajax({
                type:"POST",
                url:"/lovemusic/likeMusic",
                data:{"id":obj},
                dataType:"json",
                success:function(body){
                    //然后这里判断看是否收藏成功了
                    if(body.data == true){
                        alert("收藏成功,^^,厉害了老铁!");
                        window.location.href="loveMusic.html";
                    }else{
                        alert("收藏失败,^^,咋回事儿呢!");
                    }
                }
            });

        }

25 实现注册功能

在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
    <title>注册页面</title>

    <!-- 1. 导入CSS的全局样式 -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <!-- 2. jQuery导入,建议使用1.9以上的版本 -->
    <script src="js/jquery-3.1.1.min.js"></script>
    <script src="js/md5.min.js"></script>
    <!-- 3. 导入bootstrap的js文件 -->
    <!--<script src="js/bootstrap.min.js"></script>-->
    <script type="text/javascript"></script>
    <style>
		  #body{
			background-image: url("images/rose3.png");
            background-repeat: repeat;
            background-position: 0 20%;
            background-size: contain;
      }
      </style>
    <script>
		//    登录核心业务逻辑
		//原本是$(document).ready(function(){}) 表示当这个页面的dom树加载完成后才会执行这个,而后面的(document).ready可以省略,因此这里就是这样写也是可以的

		$(function(){
			//此时设置提交按钮的click事件,通过id选择器来获取
			$("#submit").click(function(){
				//此时就需要获取到用户名和密码的值
				let username = $("#user").val();
				let password = $("#password").val();

				//检查一下用户名和密码是否为空,以及去掉空格使用trim方法
				if(username.trim() == "" || password.trim() == ""){
					alert("用户名或密码不能为空!");
					return;
				}
				//如果都不为空,就需要使用ajax来发送请求到后端,然后处理这个请求及返回响应
				$.ajax({
					type:"POST",
					url:"/user/logon",
					//数据
					data:{"username":username,"password":password},
					//服务器返回数据类型
					dataType:"json",
					success:function(data){
						//看状态码
						if(data.status == 0){
							console.log(data);
							alert("注册成功了我的老宝贝,要跳转了哦!");
							//登录成功,这里就可以进行页面的跳转
							window.location.href="login.html";
						}else{
							alert("注册失败,用户名已存在,--!");
						}

					}

				});

			});

		});


	  </script>
</head>
<body id="body">
<div class="container" style="width: 400px;margin-top: 110px;background-color: rgba(255,255,255,0.9)">
    <h3 style="text-align: center;">回溯</h3>
    <!--        <form action="login" method="post">-->
    <div class="form-group" >
        <label for="user">用户名:</label>
        <input type="text" name="username" class="form-control" id="user" placeholder="请输入用户名"/>
    </div>

    <div class="form-group">
        <label for="password">密码:</label>
        <input type="password" name="password" class="form-control" id="password" placeholder="请输入密码"/>
    </div>

    <hr/>
    <div class="form-group" style="text-align: center;"><!--class="form-group"-->
        <input style="width: 200px;height: 40px" id="submit" class="btn btn btn-primary" type="button" value="注册" >
    </div>
    <!--	  	</form>-->
    <!-- 出错显示的信息框 -->
</div>
</body>
</html>

26 配置拦截器

当我们直接访问列表页的时候,我们需要用拦截器拦截,否则 不登录也能直接访问,这是不行的

在这里插入图片描述

在这里插入图片描述

以上所有功能全部实现完毕。

项目部署

  1. 修改配置文件

在这里插入图片描述

修改音乐存放路径:

在这里插入图片描述

在配置文件当中,修改如下:

在这里插入图片描述

  1. 将数据库在服务器上重新进行建表等操作

输入指令 mysql,进入数据库:

在这里插入图片描述

将如下内容添加进去:

-- 数据库
drop database if exists `onlinemusic`;
create database if not exists `onlinemusic` character set utf8;

-- 使用数据库
use `onlinemusic`;

-- 创建 user表
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`username` varchar(20) NOT NULL,
`password` varchar(255) NOT NULL
);

-- 创建 music 表
DROP TABLE IF EXISTS `music`;
CREATE TABLE `music` (
`id` int PRIMARY KEY AUTO_INCREMENT,
`title` varchar(50) NOT NULL,
`singer` varchar(30) NOT NULL,
`time` varchar(13) NOT NULL,
`url` varchar(1000) NOT NULL,
`userid` int(11) NOT NULL
);

-- 创建 lovemusic
DROP TABLE IF EXISTS `lovemusic`;
CREATE TABLE `lovemusic` (
`id` int PRIMARY KEY AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`music_id` int(11) NOT NULL
);

输入指令 exit 退出数据库

  1. 导包

在这里插入图片描述
在这里插入图片描述
4. 上传
在这里插入图片描述
在这里插入图片描述

  1. 云服务器配置防火墙端
    在这里插入图片描述

  2. 启动项目

刚开始可以使用 java -jar xxxx.jar 启动项目【前台运行的方式】
运行好之后没有问题,我们可以使用下面的命令来对项目进行运行

nohup java -jar xxx.jar >> log.log &

nohup :后台运行项目的指令
使用 >> log.log 将运行的日志记录到 log.log 中
& 表示 一直运行

tips:重新部署

如果更新了项目,先将 jar删除

输入如下指令:

rm -ri xxx.jar

查看进程

ps -ef | grep java

在这里插入图片描述

kill [ID] 即可删除进程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/90417.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

纳米源表测试软件更新,新增太阳能电池测试、双通道脉冲扫描

源表在电测行业中应用十分广泛&#xff0c;尤其是在需求高精度的半导体、纳米器件和材料、太阳能电池、印刷电子技术等领域有着举足轻重的地位&#xff1b;而源表软件则可以实现源表的远程控制&#xff0c;通过在软件控制源表进行配置或者测量&#xff0c;也可以对测量的数据和…

大学生游戏静态HTML网页设计 (HTML+CSS+JS仿英雄联盟网站15页)

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

2.5.9 使用 systemd 管理 MySQL 服务器实例

2.5.9 使用 systemd 管理 MySQL 服务器实例systemd 概述为 MySQL 配置 systemd使用 systemd 配置多个 MySQL 实例从 mysqld_safe 迁移到 systemd如果在以下 Linux 平台上使用 RPM 或 Debian 软件包安装 MySQL &#xff0c;则服务器启动和关闭由 systemd 管理&#xff1a; RPM …

时间序列-预测-模型-2020:Informer【比Transformer更有效的长时间序列预测】【在对性能影响不大的前提下降低复杂度】

摘要 时序预测这个领域的工作与很多其他领域类似,我们可以按“深度学习”方法的引入作为分界线。在此之前的方法是传统的时序建模方法,比如移动平均、自回归、以及结合差分的ARIMA模型等,有着悠久的历史以及基于理论基础的可解释性。但是,这类方法一般要求时间序列是平稳的…

视频转文字怎么弄?建议收藏这些方法

小伙伴们在闲暇之余&#xff0c;会不会通过一些网课的学习&#xff0c;来提高自己呢&#xff1f;有时我们网课学习来不及做笔记&#xff0c;但是重复观看又比较麻烦&#xff0c;这时我们就可以通过将视频转换成文字&#xff0c;这样就能够清楚明了的知道视频的讲解内容了。那你…

springcloud整合Hystrix

作用 1、服务降级 触发情况&#xff1a;程序运行异常、超时、服务熔断触发服务降级、线程池/信号量打满也会触发服务降级 2、服务熔断 直接拒绝访问&#xff0c;即使有正确的访问也会短路 3、服务限流 排队有序进行 构建服务 1、建module provider-hystrix-payment8001 2、改…

mac 好用的ps修图软件 Pixelmator Pro

Pixelmator Pro Pixelmator Pro 是一款任何人都可以使用的专业图像编辑工具。Pixelmator Pro旨在使每个人都可以使用最强大的专业图像编辑工具。拥有大量用于编辑和修饰照片&#xff0c;创建图形设计&#xff0c;绘画&#xff0c;绘制矢量图形以及添加令人惊叹的效果的工具&…

sdl 渲染旋转视频的方法

文章目录前言一、如何实现&#xff1f;1、计算边框大小2、计算缩放大小3、逆运算视频宽高二、完整代码三、使用示例总结前言 一般情况下播放视频时不需要旋转&#xff0c;但是如果是移动端录制的视频有时会出现rotate参数&#xff0c;且视频宽高也是互换的&#xff0c;如果直接…

为什么使用MQ?RabbitMQ概念

个人博客地址&#xff1a; http://xiaohe-blog.top/ 文章目录1. MQ基本概念1.1 MQ简述1.2 MQ的优势1.3 MQ的劣势2. RabbitMQ基本概念2.1 RabbitMQ特点2.2 AMQP协议1. MQ基本概念 1.1 MQ简述 MQ 全称 &#xff1a;Message Queue&#xff08;消息队列&#xff09;&#xff0c;是…

音效、配乐素材网站,免费商用

视频剪辑没素材&#xff1f;找不到热门音效&#xff1f;找不到好听的配乐&#xff1f; 有了这几个网站&#xff0c;承包你的所有音效、配乐&#xff0c;重点是免费&#xff01;&#xff01;&#xff01;1、菜鸟图库 https://www.sucai999.com/audio.html?vNTYwNDUx菜鸟图库网&…

【猿如意】如何在mac上搭建QT环境

作为一个代码菜鸡&#xff0c;平时主要用windows或者linux进行开发&#xff0c;但是家里有一个mac&#xff0c;平时要搭环境要找半天就很费劲。今天终于发现一个好用的平台&#xff0c;常用的开发工具都有惹。真香&#xff01; 什么是猿如意&#xff1f; 猿如意是一款面向开发…

[附源码]Nodejs计算机毕业设计基于的餐厅管理系统Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

如何注册网站?网站注册费用多少?

很多企业或者公司都会考虑注册自己的网站&#xff0c;那么如何注册网站呢&#xff1f;网站注册费用多少呢&#xff1f;具体来说注册网站是包含注册域名和制作网站&#xff0c;下面一同为大家解答如何注册网站。 一、注册域名 一个网站是必须要有域名才能访问&#xff0c;注册可…

一个 MySQL 隐式转换的坑,差点把服务器整崩溃了

本来是一个平静而美好的下午&#xff0c;其他部门的同事要一份数据报表临时汇报使用&#xff0c;因为系统目前没有这个维度的功能&#xff0c;所以需要写个SQL马上出一下&#xff0c;一个同事接到这个任务&#xff0c;于是开始在测试环境拼装这条 SQL&#xff0c;刚过了几分钟&…

职场社交app开发,迎合市场发展需求

互联网时代的到来&#xff0c;让人们在网络上花费的时间越来越多&#xff0c;依赖性也越来越大&#xff0c;我们的生活发生了很大的变化。一个人从出生开始就要面对这个复杂的社会&#xff0c;需要和各种各样的人去交流相处&#xff0c;当我们进入职场之后社交更是变成一种刚需…

【电巢】最新进展 深圳13家国企和民企入局,打造全球电子元器件集散中心

前 言 日前&#xff0c;电子元器件和集成电路国际交易中心创立大会在深圳召开。电巢获悉&#xff0c;该交易中心将于12月底正式揭牌&#xff0c;近期启动试运行&#xff0c;并在试运行期间开启线上交易。 根据此前国家发改委、商务部发布的《关于深圳建设中国特色社会主义先…

[附源码]Python计算机毕业设计高校请假管理系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

【案例实践】WRF-Python融合技术:WRF 模式前后处理、自动化运行、数据处理、可视化绘图

【查看原文】Python在WRF模型自动化运行及前后处理中实践技术应用 当今从事气象及其周边相关领域的人员&#xff0c;常会涉及气象数值模式及其数据处理&#xff0c;无论是作为业务预报的手段、还是作为科研工具&#xff0c;掌握气象数值模式与高效前后处理语言是一件非常重要的…

我填写“2022年国内软件质量调查问卷”的感想

文章目录感想起因关于软件质量的理解各行各业的质量问题数据质量问题各行各业的质量问题质量问题对我们的影响软件质量软件质量问题的影响有多大软件质量问题软件测试软件测试方法的目的包括&#xff1a;软件的基本测试方法:软件测试的各个阶段测试流程软件测试工程师总结感想起…

免费的进销存系统哪个好一些?

进销存是什么&#xff1f;如何才能选到满意的进销存管理系统&#xff1f; 进销存即购销链条&#xff0c;一般分为“进”“销”“存”“财”四个模块的工作&#xff0c;涉及采购部、物资部、生产部、市场部、销售部、财务部等多部门的协同。通过进销存管理信息化&#xff0c;能…