文章目录
- 1. 项目设计
- 2. 效果展示
- 3. 创建项目 配置文件
- 4. 数据库的设计与实现
- 5. 交互接口的设计
- 6. 工具包
- 7. 登录模块
- 8. 注册模块
- 9. 退出功能
- 10. 上传音乐模块
- 11. 播放音乐模块
- 12. 删除音乐模块
- 13. 收藏音乐模块
- 14. 取消收藏音乐模块
- 15. 主页面 - 查询模块
- 16. 收藏页面 - 查询模块
1. 项目设计
前端 : HTML+CSS+JavaScript+JQuery
后端 : Spring MVC+Spring Boot+MyBatis
2. 效果展示
3. 创建项目 配置文件
3.1 创建项目
3.2 配置文件
3.2.1 在 application.properties 中添加配置文件
配置数据库
spring.datasource.url=jdbc:mysql://localhost:3306/onlinemusicserver?characterEncoding=utf8true
spring.datasource.username=root
spring.datasource.password=0000
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
配置 Mybatis
mybatis.mapper-locations=classpath:mapper/**Mapper.xml
配置文件上传大小
spring.servlet.multipart.max-file-size = 15MB
spring.servlet.multipart.max-request-size=100MB
配置上传的路径
upload.path=E:/logs/
3.2.2 在 resources 目录下创建mapper
mapper下添加 目录 **.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.onlinemusicserver.mapper."对应的Mapper"">
</mapper>
4. 数据库的设计与实现
这里设计数据库.
drop database if exists `onlinemusicserver`;
create database `onlinemusicserver`;
use `onlinemusicserver`;
drop table if exists `user`;
create table `user`(
`userId` int primary key auto_increment,
`username` varchar(20) unique,
`password` varchar(255) not null
);
drop table if exists `music`;
create table `music`(
`musicId` int primary key auto_increment,
`title` varchar(100) not null,
`author` varchar(20) not null,
`uploadtime` timestamp default CURRENT_TIMESTAMP,
`path` varchar(1000) not null,
`userId` int not null
);
drop table if exists `collect`;
create table `collect`(
`collectId` int primary key auto_increment,
`userId` int not null,
`musicId` int not null
);
5. 交互接口的设计
请求
POST /music/upload HTTP/1.1
{singer, MultipartFile file}
响应
{
status: 1/-1 (1 为成功, -1 为失败),
message: "对应信息",
data: "内容"
}
请求
POST /collect/loveMusic HTTP/1.1
{musicId: 1}
响应
{
status: 1/-1,
message: "",
data: ""
}
请求
POST /collect/deleteLoveMusic HTTP/1.1
{musicId: 1}
响应
{
status: 1/-1,
message: "",
data: ""
}
请求
POST /collect/findLoveMusic HTTP/1.1
{musicName: "可以为空可以不为空, 为空的时候,查询所有, 不为空的时候, 模糊查询"}
响应
{
status: 1/-1,
message: "",
data: {
{
musicId: "",
title: "",
author: "",
uploadtime: "",
path: "",
userId: "",
}
...
}
}
请求
POST /music/findMusic HTTP/1.1
{musicName: "可以为空可以不为空, 为空的时候,查询所有, 不为空的时候, 模糊查询"}
响应
{
status: 1/-1,
message: "",
data: {
{
musicId: "",
title: "",
author: "",
uploadtime: "",
path: "",
userId: "",
}
...
}
}
请求
POST /music/delete HTTP/1.1
{musicId: ""}
响应
{
status: 1/-1,
message: "",
data: ""
}
请求
POST /music/deleteMore HTTP/1.1
{musicId: "1 2 3 4 5"(数组)}
响应
{
status: 1/-1,
message: "",
data: ""
}
请求
GET /music/play?path="..." HTTP/1.1
响应
{
音乐的字节信息
}
请求
POST /user/login HTTP/1.1
{username: "",password: ""}
响应
{
status: 1/-1,
message: "",
data: ""
}
请求
GET /user/logout HTTP/1.1
响应
HTTP/1.1 200
请求
POST /user/register HTTP/1.1
{username: "",password: ""}
响应
{
status: 1/-1,
message: "",
data: ""
}
6. 工具包
6.1 设置统一响应类
public class ResponseBodyMessage<T> {
private int status;
private String message;
private T data;
public ResponseBodyMessage(int status, String message, T data) {
this.status = status;
this.message = message;
this.data = data;
}
}
6.2 Constant类
public class Constant {
public static final String USER_SESSION_KEY = "user";
}
6.3 了解 MD5 加密 和 BCrypt 加密
MD5
是一个安全的散列算法,输入两个不同的明文不会得到相同的输出值,根据输出值,不能得到原始的明文,即其过程不可逆; 但是虽然不可逆,但是不是说就是安全的。因为自从出现彩虹表后,这样的密码也"不安全"。
更安全的做法是加盐或者长密码等做法,让整个加密的字符串变的更长,破解时间变慢。
Bcrypt
就是一款加密工具,可以比较方便地实现数据的加密工作。你也可以简单理解为它内部自己实现了随机加盐处理 。我们使用MD5加密,每次加密后的密文其实都是一样的,这样就方便了MD5通过大数据的方式进行破解。
Bcrypt生成的密文是60位的。而MD5的是32位的。Bcrypt破解难度更大。
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>
实现类
public class MD5Util {
private static final String salt = "1q2w3e4r5t";//可任意设置
public static String md5(String src) {
return DigestUtils.md5Hex(src);
}
/**
* 第一次加密 :模拟前端自己加密,然后传到后端
* @param inputPass
* @return
*/
public static String inputPassToFormPass(String inputPass) {
String str = ""+salt.charAt(1)+salt.charAt(3) + inputPass
+salt.charAt(5) + salt.charAt(6);
return md5(str);
}
/**
* 第二次加密
* @param formPass 前端加密过的密码,传给后端进行第2次加密
* @param salt 后端当中的盐值
* @return
*/
public static String formPassToDBPass(String formPass, String salt) {
String str = ""+salt.charAt(0)+salt.charAt(2) + formPass +salt.charAt(5)
+ salt.charAt(4);
return md5(str);
}
/**
* 上面两个函数合到一起进行调用
* @param inputPass
* @param saltDB
* @return
*/
public static String inputPassToDbPass(String inputPass, String saltDB) {
String formPass = inputPassToFormPass(inputPass);
String dbPass = formPassToDBPass(formPass, saltDB);
return dbPass;
}
}
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})
创建BCryptTest测试类:
public class BCryptTest {
public static void main(String[] args) {
//模拟从前端获得的密码
String password = "123456";
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
String newPassword = bCryptPasswordEncoder.encode(password);
System.out.println("加密的密码为: "+newPassword);
//使用matches方法进行密码的校验
boolean same_password_result = bCryptPasswordEncoder.matches(password,newPassword);
//返回true
System.out.println("加密的密码和正确密码对比结果: "+same_password_result);
boolean other_password_result = bCryptPasswordEncoder.matches("987654",newPassword);
//返回false
System.out.println("加密的密码和错误的密码对比结果: " + other_password_result);
}
运行结果: (每次加密的密码都不同)
6.4 在Config中 注入 BCryptPasswordEncoder 对象
@Configuration
public class AppConfig implements WebMvcConfigurer {
@Bean
public BCryptPasswordEncoder getBCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
6.5 添加拦截器
6.5.1 LoginInterceptor 类
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession httpSession = request.getSession(false);
if(httpSession != null && httpSession.getAttribute(Constant.USER_SESSION_KEY) != null) {
return true;
}
response.sendRedirect("/login.html");
return false;
}
}
6.5.2 AppConfig 类
@Configuration
public class AppConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
LoginInterceptor loginInterceptor = new LoginInterceptor();
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/**/login.html")
.excludePathPatterns("/**/css/**.css")
.excludePathPatterns("/**/images/**")
.excludePathPatterns("/**/fonts/**")
.excludePathPatterns("/**/js/**.js")
.excludePathPatterns("/**/scss/**")
.excludePathPatterns("/**/user/login")
.excludePathPatterns("/**/user/register")
.excludePathPatterns("/**/user/logout");
}
}
7. 登录模块
7.1 创建 User 实体类
@Data
public class User {
private int userId;
private String username;
private String password;
}
7.2 使用 Mybatis 操作数据库
7.2.1 在 UserServer 中添加代码
public User selectByName(String username) {
return userMapper.selectByName(username);
}
7.2.2 在 UserMapper 中添加代码
/**
* 通过用户名去查找用户信息, 用来对比登录信息.
* @param username 用户名
* @return 对应用户名的用户信息
*/
User selectByName(String username);
7.2.3 在 UserMapper.xml 中添加代码
<select id="selectByName" resultType="com.example.onlinemusicserver.model.User">
select * from user where username=#{username};
</select>
7.3 创建 UserConroller 添加代码
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserServer userServer;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
/**
* 用户登录
* @param user
* @param req
* @return
*/
@RequestMapping("/login")
public ResponseBodyMessage<User> login(@RequestBody User user, HttpServletRequest req) {
User truUser = userServer.selectByName(user.getUsername());
if(truUser != null) {
System.out.println("登陆成功");
System.out.println(user.getPassword() + " " + truUser.getPassword());
boolean flg = bCryptPasswordEncoder.matches(user.getPassword(),truUser.getPassword());
if(!flg) {
return new ResponseBodyMessage<>(-1,"当前账号密码错误!",user);
}
HttpSession session = req.getSession(true);
session.setAttribute(Constant.USER_SESSION_KEY,truUser);
return new ResponseBodyMessage<>(1,"登录成功!",truUser);
}else{
System.out.println("登录失败");
return new ResponseBodyMessage<>(-1,"当前账号密码错误!",user);
}
}
}
7.4 前端代码
let loginButton = document.querySelector('#loginButton');
loginButton.onclick = function() {
let username = document.querySelector('#loginUsername');
let password = document.querySelector('#loginPassword');
if (username.value.trim() == ""){
alert('请先输入用户名!');
username.focus();
return;
}
if (password.value.trim() == ""){
alert('请先输入密码!');
password.focus();
return;
}
$.ajax({
url: "user/login",
method: "POST",
data: JSON.stringify({username: username.value.trim(), password: password.value.trim()}),
contentType: "application/json;charset=utf-8",
success: function(data, status) {
if(data.status == 1) {
location.assign("index.html");
}else{
alert(data.message);
username.value="";
password.value="";
username.focus();
}
}
})
}
8. 注册模块
8.1 使用 Mybatis 操作数据库
8.1.1 在 UserServer 中添加代码
public int addnewUser(User newUser) {
return userMapper.addnewUser(newUser);
}
8.1.2 在 UserMapper 中添加代码
/**
* 注册新的用户
* @param newUser 新用户信息
* @return
*/
int addnewUser(User newUser);
8.1.3 在 UserMapper.xml 中添加代码
<insert id="addnewUser">
insert into user(username,password) values (#{username},#{password});
</insert>
8.2 向 UserController 中添加代码
/**
* 注册用户
* @param user 用户信息
* @return
*/
@RequestMapping("/register")
public ResponseBodyMessage<Boolean> register(@RequestBody User user) {
User user1 = userServer.selectByName(user.getUsername());
if(user1 != null) {
return new ResponseBodyMessage<>(-1,"当前用户已经存在",false);
}else {
User newUser = new User();
newUser.setUsername(user.getUsername());
String newPassword = bCryptPasswordEncoder.encode(user.getPassword());
newUser.setPassword(newPassword);
userServer.addnewUser(newUser);
return new ResponseBodyMessage<>(1,"注册成功",true);
}
}
8.3 前端代码
let Reg = document.querySelector('#Reg');
Reg.onclick = function() {
let username = document.querySelector('#RegUsername');
let password1 = document.querySelector('#RegPassword1');
let password2 = document.querySelector('#RegPassword2');
if(!$('#checkbox').is(':checked')) {
alert("请勾选条款");
return;
}
if(username.value.trim() == ""){
alert("请先输入用户名!");
username.focus();
return;
}
if(password1.value.trim() == ""){
alert('请先输入密码!');
password1.focus();
return;
}
if(password2.value.trim() == ""){
alert('请再次输入密码!');
password2.focus();
return;
}
if(password1.value.trim() != password2.value.trim()) {
alert('两次输入的密码不同!');
passwrod1.value="";
password2.value="";
return;
}
$.ajax({
url: "user/register",
method: "POST",
data: JSON.stringify({username: username.value.trim(), password: password1.value.trim()}),
contentType: "application/json;charset=utf-8",
success: function(data,status){
if(data.status == 1) {
alert(data.message);
location.assign("login.html");
}else{
alert(data.message);
username.value="";
password1.value="";
password2.value="";
username.focus();
}
}
})
}
9. 退出功能
9.1 向 UserController 中添加代码
@RequestMapping("/logout")
public void userLogout(HttpServletRequest request, HttpServletResponse response) throws IOException {
HttpSession session = request.getSession(false);
// 拦截器的拦截, 所以不可能出现session为空的情况
session.removeAttribute(Constant.USER_SESSION_KEY);
response.sendRedirect("login.html");
}
9.2 登录注册测试.
10. 上传音乐模块
10.1 创建 Music 实体类
@Data
public class Music {
private int musicId;
private String title;
private String author;
private Timestamp uploadtime;
private String path;
private int userId;
private String srcPath;
}
10.2 使用 Mybatis 操作数据库
10.2.1 在 MusicServer 中添加代码
public int insert(String title, String author, String path, int userId){
return musicMapper.insert(title,author,path,userId);
}
public List<Music> selectByTitle(String title) {
return musicMapper.selectByTitle(title);
}
10.2.2 在 MusicMapper 中添加代码
/**
* 上传音乐
* @param title 音乐名
* @param author 歌手
* @param path 对应的地址
* @param userId 上传的用户Id
* @return 返回影响行数
*/
int insert(String title, String author, String path, int userId);
/**
* 通过音乐名去查找歌曲.
* @param title 音乐名
* @return 对应音乐名的所有歌曲
*/
List<Music> selectByTitle(String title);
10.2.3 在 MusicMapper.xml 中添加代码
<insert id="insert">
insert into music(title,author,path,userId) values (#{title},#{author},#{path},#{userId});
</insert>
<select id="selectByTitle" resultType="com.example.onlinemusicserver.model.Music">
select * from music where title = #{title};
</select>
10.3 向 MusicController 中添加代码
@RestController
@RequestMapping("/music")
public class MusicController {
@Autowired
private MusicServer musicServer;
@Value("${upload.path}")
public String SAVE_PATH;
/**
* 上传音乐
* @param singer
* @param file
* @param request
* @return
*/
@RequestMapping("/upload")
public ResponseBodyMessage<Boolean> insertMusic(@RequestParam String singer,
@RequestPart("filename") MultipartFile file,
HttpServletRequest request,
HttpServletResponse response) {
// 检测登录
HttpSession session = request.getSession(false);
if (session == null || session.getAttribute(Constant.USER_SESSION_KEY) == null) {
System.out.println("当前未登录!");
return new ResponseBodyMessage<>(-1,"请登录后上传",false);
}
// 文件的类型
String fileNameAndType = file.getOriginalFilename();
// 防止出现重复的相同歌曲和相同歌手.可以出现相同歌曲不同歌手
String title = fileNameAndType.substring(0,fileNameAndType.lastIndexOf('.'));
// 可能出现多首名称相同的歌曲, 所以用 List
List<Music> list = musicServer.selectByTitle(title);
if(list != null){
for(Music music : list) {
if(music.getAuthor().equals(singer)){
return new ResponseBodyMessage<>(-1,"当前歌手的歌曲已经存在!",false);
}
}
}
// 创建文件
String path = SAVE_PATH +singer+"-"+fileNameAndType;
File dest = new File(path);
if(!dest.exists()) {
dest.mkdirs();
}
try {
file.transferTo(dest);
//return new ResponseBodyMessage<>(1,"上传成功!",true);
} catch (IOException e) {
e.printStackTrace();
return new ResponseBodyMessage<>(-1,"服务器上传失败!",false);
}
// 这里对是不是 MP3 文件进行判断. 主要是判断是否存在 TAG 这个字符
File file1 = new File(path);
byte[] res = null;
try {
res = Files.readAllBytes(file1.toPath());
if(res == null) {
return new ResponseBodyMessage<>(-1,"当前文件不存在",false);
}
String str = new String(res);
if(!str.contains("TAG")) {
file1.delete();
return new ResponseBodyMessage<>(-1,"当前不是mp3文件",false);
}
}catch (IOException e){
e.printStackTrace();
return new ResponseBodyMessage<>(-1,"服务器出现问题", false);
}
// 在数据库中上传数据
User user = (User) session.getAttribute(Constant.USER_SESSION_KEY);
// 这里传递的 path 没有带 `.MP3` 后期在前端进行设置
String uploadPath = "/music/play?path="+singer+"-"+title;
try {
int ret = musicServer.insert(title,singer,uploadPath,user.getUserId());
if(ret == 1) {
response.sendRedirect("/index.html");
return new ResponseBodyMessage<>(1,"上传成功",true);
}else {
return new ResponseBodyMessage<>(-1,"数据库上传失败",false);
}
}catch (BindingException | IOException e) {
dest.delete();
return new ResponseBodyMessage<>(-1,"数据库上传失败",false);
}
}
}
10.4 前端代码
<form method="post" enctype="multipart/form-data" action="music/upload">
文件上传: <input type="file" name="filename" id="filename" />
歌手名: <label>
<input type="text" name="singer" placeholder="请输入歌手名" id="singer" />
</label>
<input type="submit" value="上传" id="submit"/>
</form>
10.5 测试代码
11. 播放音乐模块
11.1 向 MusicController 中添加代码
/**
* 播放音乐
* @param path
* @return
*/
@RequestMapping("/play")
public ResponseEntity<byte[]> playMusic(@RequestParam String path){
File file = new File(SAVE_PATH + path);
byte[] res = null;
try {
res = Files.readAllBytes(file.toPath());
if (res == null) {
return ResponseEntity.badRequest().build();
}
return ResponseEntity.ok(res);
} catch (IOException e) {
e.printStackTrace();
return ResponseEntity.badRequest().build();
}
}
11.2 测试代码
观察字节码可以看出, 有 TAG 这个字符
12. 删除音乐模块
12.1 使用 Mybatis 操作数据库
12.1.1 在 MusicServer 和 CollectServer 中添加代码
musicServer
public Music selectById(int musicId) {
return musicMapper.selectById(musicId);
}
public int deleteById(int musicId) {
return musicMapper.deleteById(musicId);
}
collectServer
public int deleteLoveMusicById(int musicId){
return collectMapper.deleteLoveMusicById(musicId);
}
12.1.2 在 MusicMapper 和 CollectMapper 中添加代码
musicMapper
/**
* 通过音乐Id去查找歌曲
* @param musicId 音乐Id
* @return 查找到的音乐Id
*/
Music selectById(int musicId);
/**
* 删除对应音乐Id的歌曲
* @param musicId 音乐Id
* @return 返回影响行数
*/
int deleteById(int musicId);
collectMapper
/**
* 删除收藏表中音乐Id为musicId的
* @param musicId 音乐Id
* @return 返回受影响行数
*/
int deleteLoveMusicById(int musicId);
12.1.3 在 MusicMapper.xml 和 CollectMapper.xml 中添加代码
MusicMapper.xml
<select id="selectById" resultType="com.example.onlinemusicserver.model.Music">
select * from music where musicId = #{musicId};
</select>
<delete id="deleteById">
delete from music where musicId = #{musicId};
</delete>
CollectMapper.xml
<delete id="deleteLoveMusicById">
delete from collect where musicId = #{musicId};
</delete>
12.2 删除单一音乐功能
12.2.1 向 MusicController 中添加代码
/**
* 删除音乐
* @param musicId
* @return
*/
@RequestMapping("/delete")
public ResponseBodyMessage<Boolean> deleteMusic(@RequestParam String musicId) {
// 1. 检测音乐是不是存在
Music music = musicServer.selectById(Integer.parseInt(musicId));
// 2. 不存在直接返回, 存在就删除
if (music == null) {
System.out.println("该音乐不存在");
return new ResponseBodyMessage<>(-1,"没有你要删除的音乐",false);
}
// 2.1 删除数据库中的记录
int ret = musicServer.deleteById(Integer.parseInt(musicId));
if(ret == 1) {
// 2.2 删除服务器上的数据
int index = music.getPath().lastIndexOf("=");
String PathName = music.getPath().substring(index+1);
File file = new File(SAVE_PATH + PathName+".mp3");
if(file.delete()){
collectServer.deleteLoveMusicById(Integer.parseInt(musicId));
return new ResponseBodyMessage<>(1,"删除成功!",true);
}else{
return new ResponseBodyMessage<>(-1,"服务器删除失败!",false);
}
}else {
return new ResponseBodyMessage<>(-1,"数据库删除失败!",false);
}
}
12.2.2 前端代码
function deleteMusic(musicId) {
$.ajax({
url: "music/delete",
method: "post",
data:{"musicId":musicId},
dataType: "json",
success:function(data,status) {
if(data.status == 1) {
alert(data.message);
location.assign("index.html");
}else{
alert(data.message);
}
}
})
}
12.2.3 测试代码
12.3 删除多个音乐功能
12.3.1 向 MusicConroller 中添加代码
/**
* 删除多选音乐
* @param musicId
* @return
*/
@RequestMapping("/deleteMore")
public ResponseBodyMessage<Boolean> deleteMoreMusic(@RequestParam("musicId[]") List<Integer> musicId) {
int sum = 0;
for (int i = 0; i < musicId.size(); i++) {
Music music = musicServer.selectById(musicId.get(i));
if(music == null) {
return new ResponseBodyMessage<>(-1,"没有你要删除的音乐",false);
}
int ret = musicServer.deleteById(musicId.get(i));
if (ret == 1) {
int index = music.getPath().lastIndexOf("=");
String PathName = music.getPath().substring(index+1);
File file = new File(SAVE_PATH + PathName+".mp3");
if(file.delete()){
collectServer.deleteLoveMusicById(musicId.get(i));
sum += ret;
}else{
return new ResponseBodyMessage<>(-1,"服务器删除失败!",false);
}
}else {
return new ResponseBodyMessage<>(-1,"数据库删除失败!",false);
}
}
if(sum == musicId.size()) {
return new ResponseBodyMessage<>(1,"音乐删除成功!",true);
}else{
return new ResponseBodyMessage<>(-1,"音乐删除失败!",false);
}
}
12.3.2 前端代码
$(function(){
$.when(load).done(function() {
$("#deleteMore").click(function(){
let musicId = new Array();
let i =0;
$("input:checkbox").each(function(){
if($(this).is(":checked")) {
musicId[i] = $(this).attr("id");
i++;
}
});
$.ajax({
url: "music/deleteMore",
method: "post",
data:{"musicId":musicId},
dataType:"json",
success:function(data,status) {
if(data.status == 1) {
alert(data.message);
location.assign("index.html");
}else{
alert(data.message);
}
}
})
})
})
})
12.3.3 测试代码
13. 收藏音乐模块
13.1 创建 Collect 实体类
@Data
public class Collect {
private int collectId;
private int userId;
private int musicId;
}
13.2 使用 Mybatis 操作数据库
13.2.1 在 CollectServer 中添加代码
public Collect findCollectMusic(int userId, int musicId) {
return collectMapper.findCollectMusic(userId,musicId);
}
public int insertLoveMusic(int userId, int musicId) {
return collectMapper.insertLoveMusic(userId, musicId);
}
13.2.2 在 CollectMapper 中添加代码
/**
* 查看对应用户是否已经收藏了该音乐
* @param userId 用户Id
* @param musicId 音乐Id
* @return 收藏歌单
*/
Collect findCollectMusic(int userId, int musicId);
/**
* 收藏音乐
* @param userId 用户Id
* @param musicId 音乐Id
* @return 返回影响行数
*/
int insertLoveMusic(int userId, int musicId);
13.2.3 在 CollectMapper.xml 中添加代码
<select id="findCollectMusic" resultType="com.example.onlinemusicserver.model.Collect">
select * from collect where userId = #{userId} and musicId = #{musicId};
</select>
<insert id="insertLoveMusic">
insert into collect(userId,musicId) values(#{userId},#{musicId});
</insert>
13.3 向 CollectControll 中添加代码
/**
* 点击收藏的时候, 收藏音乐
* @param musicId
* @param request
* @return
*/
@RequestMapping("/loveMusic")
public ResponseBodyMessage<Boolean> AddLoveMusic(@RequestParam String musicId, HttpServletRequest request) {
int music_Id = Integer.parseInt(musicId);
HttpSession session = request.getSession(false);
User user = (User) session.getAttribute(Constant.USER_SESSION_KEY);
int userId = user.getUserId();
Collect collect = collectServer.findCollectMusic(userId,music_Id);
if (collect != null) {
return new ResponseBodyMessage<>(-1, "当前已经收藏了",false);
}else{
int ret = collectServer.insertLoveMusic(userId,music_Id);
if(ret == 1) {
return new ResponseBodyMessage<>(1, "收藏成功!",true);
}else{
return new ResponseBodyMessage<>(-1,"收藏失败",false);
}
}
}
13.4 前端代码
function collectMusic(musicId) {
$.ajax({
url: "collect/loveMusic",
method: "post",
data:{"musicId":musicId},
dataType: "json",
success:function(data,status){
if(data.status == 1) {
alert(data.message);
location.assign("collect.html");
}else{
alert(data.message);
}
}
})
}
13.5 测试代码
14. 取消收藏音乐模块
14.1 使用 Mybatis 操作数据库
14.1.1 在 CollectServer 中添加代码
public int deleteLoveMusic(int userId,int musicId){
return collectMapper.deleteLoveMusic(userId,musicId);
}
14.1.2 在 CollectMapper 中添加代码
/**
* 删除用户收藏的对应的音乐Id
* @param userId 用户Id
* @param musicId 音乐Id
* @return 受影响行数
*/
int deleteLoveMusic(int userId,int musicId);
14.1.3 在 CollectMapper.xml 中添加代码
<delete id="deleteLoveMusic">
delete from collect where userId = #{userId} and musicId = #{musicId}
</delete>
14.2 向 CollectControll 中添加代码
/**
* 删除收藏的音乐
* @param musicId
* @param request
* @return
*/
@RequestMapping("/deleteLoveMusic")
public ResponseBodyMessage<Boolean> deleteLoveMusic(@RequestParam String musicId,HttpServletRequest request) {
HttpSession session = request.getSession(false);
if(session == null) {
return new ResponseBodyMessage<>(-1,"当前未登录",false);
}
User user = (User) session.getAttribute(Constant.USER_SESSION_KEY);
int userId = user.getUserId();
int ret = collectServer.deleteLoveMusic(userId,Integer.parseInt(musicId));
if(ret == 1) {
return new ResponseBodyMessage<>(1,"取消收藏成功!",true);
}else{
return new ResponseBodyMessage<>(-1,"取消收藏失败!",false);
}
}
14.3 前端代码
function deleteLoveMusic(musicId) {
$.ajax({
url: "collect/deleteLoveMusic",
method: "post",
data:{"musicId":musicId},
dataType: "json",
success:function(data,status) {
if(data.status == 1) {
alert(data.message);
location.assign("collect.html");
}else{
alert(data.message);
}
}
})
}
14.4 测试代码
15. 主页面 - 查询模块
15.1 使用 Mybatis 操作数据库
15.1.1 在 MusicServer 中添加代码
public List<Music> findMusic() {
return musicMapper.findMusic();
}
public List<Music> findMusicByName(String name) {
return musicMapper.findMusicByName(name);
}
15.1.2 在 MusicMapper 中添加代码
/**
* 查找所有的歌曲
* @return 所有的歌曲
*/
List<Music> findMusic();
/**
* 支持模糊查询的歌曲.
* @param name 部分歌曲名
* @return 对应所有的歌曲
*/
List<Music> findMusicByName(String name);
15.1.3 在 MusicMapper.xml 中添加代码
<select id="findMusic" resultType="com.example.onlinemusicserver.model.Music">
select * from music;
</select>
<select id="findMusicByName" resultType="com.example.onlinemusicserver.model.Music">
select * from music where title like concat('%',#{name},'%');
</select>
15.2 向 MusicController 中添加代码
/**
* 支持模糊查询, 支持空查询
* @param name
* @return
*/
@RequestMapping("/findMusic")
public ResponseBodyMessage<List<Music>> findMusic(@RequestParam(required = false) String name) {
List<Music> list = null;
if(name != null) {
list = musicServer.findMusicByName(name);
}else {
list = musicServer.findMusic();
}
return new ResponseBodyMessage<>(1,"查询完毕!",list);
}
15.3 前端代码
$(function(){load()});
function load(musicName) {
$.ajax({
url: 'music/findMusic',
method: 'POST',
data: {"name":musicName},
dataType: "json",
success: function(data,status) {
if(data.data!=null){
createMusic1(data.data);
let audios = document.querySelectorAll('#player2');
for(let audio of audios) {
new MediaElementPlayer(audio, {
pluginPath: 'https://cdn.jsdelivr.net/npm/mediaelement@4.2.7/build/',
shimScriptAccess: 'always',
success: function () {
let play = document.querySelector('.player');
play.style = "visibility: visible;";
}
});
}
}
}
})
}
function createMusic1(lists) {
let s = '';
for(let list of lists) {
s+= '<div class="d-block d-md-flex podcast-entry bg-white mb-5" data-aos="fade-up">';
s+= '<img src="images/img_2.jpg" class="image">';
s+= '<div class="text">';
s+= '<input id="'+list.musicId+'" type="checkbox" class="checkbox">';
s+= '<h3 class="font-weight-light">'+list.title+'</h3>';
s+= '<div class="text-white mb-3"><span class="text-black-opacity-05"><small>'+list.author+'</small><span class="sep">/</span><small>'+DateFormat(list.uploadtime)+'</small></span></div>';
s+= '<input type="button" class="btn btn-primary" style="margin: 5px;" value="收藏音乐" οnclick="collectMusic(\''+list.musicId+'\')">';
s+= '<input type="button" class="btn btn-primary" style="margin: 5px;" value="删除音乐" οnclick="deleteMusic(\''+list.musicId+'\')">';
s+= '<div class="player">';
s+= '<audio id="player2" preload="none" controls style="max-width: 100%">';
s+= '<source src="'+ list.path+'.mp3'+'" type="audio/mp3">';
s+= '</audio></div></div></div>';
}
$("#list23").html(s);
}
// 把毫秒级时间戳转化成格式化日期
function DateFormat(timeStampMS) {
var date = new Date(timeStampMS);
var year = date.getFullYear(),
month = date.getMonth()+1,//月份是从0开始的
day = date.getDate(),
hour = date.getHours(),
min = date.getMinutes(),
sec = date.getSeconds();
var newTime = year + '-' +
(month < 10? '0' + month : month) + '-' +
(day < 10? '0' + day : day) + ' ' +
(hour < 10? '0' + hour : hour) + ':' +
(min < 10? '0' + min : min) + ':' +
(sec < 10? '0' + sec : sec);
return newTime;
}
$(function(){
$("#submit1").click( function(){
var name = $("#exampleInputName2").val();
load(name);
});
});
15.4 测试代码
16. 收藏页面 - 查询模块
16.1 使用 Mybatis 操作数据库
16.1.1 在 CollectServer 中添加代码
public List<Music> findLoveMusicByUserId(int userId){
return collectMapper.findLoveMusicByUserId(userId);
}
public List<Music> findLoveMusicByNameAndUserId(String name,int userId){
return collectMapper.findLoveMusicByNameAndUserId(name,userId);
}
16.1.2 在 CollectMapper 中添加代码
/**
* 查找用户收藏的所有音乐
* @param userId 用户Id
* @return 返回查询到的所有音乐
*/
List<Music> findLoveMusicByUserId(int userId);
/**
* 查找用户收藏音乐中名字带有 name的音乐
* @param name 部分名字
* @param userId 用户Id
* @return 返回查询到的所有音乐
*/
List<Music> findLoveMusicByNameAndUserId(String name,int userId);
16.1.3 在 CollectMapper.xml 中添加代码
<select id="findLoveMusicByUserId" resultType="com.example.onlinemusicserver.model.Music">
select m.* from collect c,music m where m.musicId = c.musicId and c.userId = #{userId};
</select>
<select id="findLoveMusicByNameAndUserId" resultType="com.example.onlinemusicserver.model.Music">
select m.* from collect c,music m where m.musicId = c.musicId and c.userId = #{userId} and m.title like concat('%',#{name},'%');
</select>
16.2 向 CollectController 中添加代码
/**
* 1. 空查询, 查找所有的收藏音乐
* 2. 模糊查询, 查询包含部分 musicName 的所有收藏音乐
* @param musicName
* @param request
* @return
*/
@RequestMapping("findLoveMusic")
public ResponseBodyMessage<List<Music>> findLoveMusic(@RequestParam(required = false) String musicName,HttpServletRequest request) {
HttpSession session = request.getSession(false);
if(session == null) {
return new ResponseBodyMessage<>(-1,"当前未登录",null);
}
User user = (User) session.getAttribute(Constant.USER_SESSION_KEY);
int userId = user.getUserId();
List<Music> list = null;
if(musicName == null) {
list = collectServer.findLoveMusicByUserId(userId);
}else{
list = collectServer.findLoveMusicByNameAndUserId(musicName,userId);
}
return new ResponseBodyMessage<>(1,"查询成功!",list);
}
16.3 前端代码
$(function(){load()});
function load(musicName) {
$.ajax({
url: 'collect/findLoveMusic',
method: 'POST',
data: {"musicName":musicName},
dataType: "json",
success: function(data,status) {
if(data.data!=null){
createMusic1(data.data);
let audios = document.querySelectorAll('#player2');
for(let audio of audios) {
new MediaElementPlayer(audio, {
pluginPath: 'https://cdn.jsdelivr.net/npm/mediaelement@4.2.7/build/',
shimScriptAccess: 'always',
success: function () {
let play = document.querySelector('.player');
play.style = "visibility: visible;";
}
});
}
}
}
})
}
$(function(){
$("#submit1").click( function(){
var name = $("#exampleInputName2").val();
load(name);
});
});
function createMusic1(lists) {
let s = '';
for(let list of lists) {
s+= '<div class="d-block d-md-flex podcast-entry bg-white mb-5" data-aos="fade-up">';
s+= '<img src="images/img_1.jpg" class="image">';
s+= '<div class="text">';
s+= '<h3 class="font-weight-light">'+list.title+'</h3>';
s+= '<div class="text-white mb-3"><span class="text-black-opacity-05"><small>'+list.author+'</small><span class="sep">/</span><small>'+DateFormat(list.uploadtime)+'</small></span></div>';
s+= '<input type="button" class="btn btn-primary" style="margin: 5px;" value="取消收藏" οnclick="deleteLoveMusic(\''+list.musicId+'\')">';
s+= '<div class="player">';
s+= '<audio id="player2" preload="none" controls style="max-width: 100%">';
s+= '<source src="'+ list.path+'.mp3'+'" type="audio/mp3">';
s+= '</audio></div></div></div>';
}
$("#list23").html(s);
}
// 把毫秒级时间戳转化成格式化日期
function DateFormat(timeStampMS) {
var date = new Date(timeStampMS);
var year = date.getFullYear(),
month = date.getMonth()+1,//月份是从0开始的
day = date.getDate(),
hour = date.getHours(),
min = date.getMinutes(),
sec = date.getSeconds();
var newTime = year + '-' +
(month < 10? '0' + month : month) + '-' +
(day < 10? '0' + day : day) + ' ' +
(hour < 10? '0' + hour : hour) + ':' +
(min < 10? '0' + min : min) + ':' +
(sec < 10? '0' + sec : sec);
return newTime;
}