创建 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
);
打开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
在package com.example.onlinemusic.model包中创建User类
@Data
public class User {
private int id;
private String username;
private String password;
}
1.新建mapper包,在mapper包下新建UserMapper
@Mapper
public interface UserMapper {
User login(User loginUser);
}
2.在resource目录下,新建mybatis文件夹,新建UserMapper.xml
@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中:
@Data
// 这是统一响应体工具类
public class ResponseBodyMessage {
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;
}
}
加了响应体之后,登录成功,我们再存储一下 Session
完整代码如下:
@RestController // 这个注解是 @Controller 和 @RequestBody 的组合注解
@RequestMapping("/user") // 一级路由
public class UserController {
@Autowired
private UserMapper userMapper; // 注入 UserMapper
// 后面要用 UserMapper里面的方法,去进行数据查询
// 登录功能
@RequestMapping("/login")
// 传递两个参数 一个是 username 一个是 password
// 加了统一响应体类之后 返回值 不在是 void ,而是 我们的 ResponseBodyMessage
public ResponseBodyMessage 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);
}
}
}
Bcrypt就是一款加密工具,可以比较方便地实现数据的加密工作。你也可以简单理解为它内部自己实现了随机加盐处理 。我们使用MD5加密,每次加密后的密文其实都是一样的,这样就方便了MD5通过大数据的方式进行破解。Bcrypt生成的密文是60位的。而MD5的是32位的。Bcrypt破解难度更大
添加如下依赖到 pom.xml:
org.springframework.security
spring-security-web
org.springframework.security
spring-security-config
在 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);
}
}
逻辑如下:
1.根据用户名名称 查询 当前是否存在这样的用户[用户名:默认是唯一的]
2.取出当前用户的密码,进行匹配,查看密码是否是一样的,一样就登录成功
这里为什么要建一个 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 代码如下:
/**
* 实现加密登录
*/
@RequestMapping("/login")
// 传递两个参数 一个是 username 一个是 password
// 加了统一响应体类之后 返回值 不在是 void ,而是 我们的 ResponseBodyMessage
public ResponseBodyMessage 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);
}
}
@Data
public class Music {
private int id;
private String title;
private String singer;
private String time;
private String url;
private int userId;
}
@RestController // 组合注解 @Controller + @ResponseBody
@RequestMapping("/music")
public class MusicController {
/**
* 上传音乐
* @return
*/
// 从配置文件中 将 路径 读取出来
@Value("${music.local.path}")
private String SAVE_PATH;
@RequestMapping("/upload")
public ResponseBodyMessage 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);
}
}
每个种类的文件都要自己的格式,检测当前你上传的格式,判断这个文件的格式是不是 mp3 文件
不能通过后缀名进行判断,后缀名可以进行更改的
insert into music(title,singer,time,url,userid)
values(#{title},#{singer},#{time},#{url},#{userid});
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 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);
}
// 另外一个问题: 如果重复上传一首歌曲 能否上传成功? 可以
}
}
/**
* 播放音乐模块设计
*/
// 播放音乐的时候,路径:/music/get?path=xxx.mp3
@RequestMapping("/get")
public ResponseEntity 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();
}
请求和响应设计:
data: 成功 or 失败
3.MusicController 内容如下:
@RequestMapping("/delete")
public ResponseBodyMessage 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);
}
}
}
约定前后端相互接口:
MusicController 内容如下:
/**
* 批量删除
* id[1,3,5,7,9]
*/
@RequestMapping("/deleteSel")
public ResponseBodyMessage deleteSelMusic(@RequestParam("id[]")
List 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);
}
}
此处查询需要满足几个功能:
请求和响应设计:
模糊查询:
select * from music where title like concat('%',#{musicName},'%');
@RequestMapping("/findmusic")
// 这里有一个小细节,我们可能不传参那么 我们的 @RequestParam 这里 需要 required = false
public ResponseBodyMessage> findMusic(@RequestParam(required = false)
String musicName) {
List musicList = null;
if(musicName != null) {
// 不为空 查询指定音乐
musicList = musicMapper.findMusicByName(musicName);
}else {
// 为空 查询所有音乐
musicList = musicMapper.findMusic();
}
return new ResponseBodyMessage<>(0,"查询到了所有的音乐",musicList);
}
请求和响应设计:
/**
* 收藏音乐,需要获取到 userId 和 musicId,musicId 传入的参数,userId 通过 session 获取
*/
@RequestMapping("/likeMusic")
public ResponseBodyMessage 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);
}
}
此处查询需要满足几个功能:
请求和响应设计,和查询音乐模块是一样的,这里不在做过多的阐述
支持模糊查询,多表联合查询
@RequestMapping("/findlovermusic")
public ResponseBodyMessage> 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 musicList = null;
if(musicName == null) {
// 如果等于空,查询所有的音乐
musicList = loveMusicMapper.findLoveMusicByUserId(userId);
}else {
// 如果不等于空,查询 指定收藏的音乐
musicList = loveMusicMapper.findLoveMusicByKeyAndUid(musicName,userId);
}
return new ResponseBodyMessage<>(0,"查询到了所有的歌曲信息",musicList);
}
请求和响应设计
@RequestMapping("/deletelovemusic")
public ResponseBodyMessage 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);
}
}
当删除 music 表 当中的 musicId 为 5的这首音乐的时候,请问 lovemusic这张表中,是不是应该也被删除?
music 表
我们在删除 音乐模块中,进行删除,需要同步删除我们 喜欢的音乐(lovemuisc),因此,我们对 MusicController 中进行优化和完善。
在 MusicController 中,删除 单个音乐中 添加以下内容:
在 批量删除中 添加以下内容:
@RequestMapping("/logon")
public ResponseBodyMessage 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参考手册
JS核心代码如下:
当我们跳转到 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 += '';
s += ' ';//
s += '' + data[i].title + ' ';
s += '' + data[i].singer + ' ';
//s += " ";
s += ' ' + ' ';
s += ' ' + ' ';
s += ' ';
}
//然后将所有的数据放到tbody里面
$('#info').html(s);
}
});
}
这里播放歌曲,我们采用开源的播放控件:
码云地址
GitHub
将该开源项目,下载到本地,取出player文件夹,放入static文件夹下
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);
}
之前约定好的 前后端交互接口
前端传入的数据和后端返回的响应,响应的 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("删除失败了老铁!");
}
}
});
}
思路很简单,我们前面实现音乐列表页的时候 完成了 load 查询函数,这里我们只需要点击 按钮,执行我们的回调函数,拿到我们的 输入框的信息,即可,进行查询
JS核心代码如下:
// 查询和删除
// 查询~~~~~~
$(function(){
// 点击 提交按钮执行 回调函数
$("#submit1").click( function(){
//这里的功能就是进行查询而框里面不传参数就是默认的查询所有音乐
//传了参数就进行模糊查询
var name = $("#exampleInputName2").val();
load(name);
});
});
这里的删除,我们的删除逻辑为,点击删除,如图:
我们需要获取到 每一行 中的 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("删除失败!");
}
}
});
收藏音乐列表页,和 list.html 基本一样,但是需要注意我们需要将ajax中的 url 进行修改:
loveMusci.html 如下:
在 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("收藏失败,^^,咋回事儿呢!");
}
}
});
}
注册页面
回溯
当我们直接访问列表页的时候,我们需要用拦截器拦截,否则 不登录也能直接访问,这是不行的
以上所有功能全部实现完毕。
修改音乐存放路径:
在配置文件当中,修改如下:
输入指令 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 退出数据库
刚开始可以使用 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] 即可删除进程