Java小案例-MusiQ音乐网站

2023-12-24 08:36
文章标签 java 案例 音乐网站 musiq

本文主要是介绍Java小案例-MusiQ音乐网站,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


目录

前言

项目功能

技术栈

后端

前端

开发环境

项目展示

前台-首页-展示

前台-首页-代码

前台-歌单-展示

前台-歌单-代码

前台-歌手-展示

前台-歌手-代码

前台-其他页面展示

后台-登录-展示

后台-登录-代码

后台-首页-展示

首台-首页-代码

后台-其他页面-展示

视频展示

详细步骤及代码

Pom.xml导包

1.启动类

2.过滤器

3.Redis配置

4.application.properties配置文件

5.log4j.properties配置文件

源码获取


前言

本音乐网站的客户端和管理端使用 Vue 框架来实现,服务端使用 Spring Boot + MyBatis 来实现,数据库使用了 MySQL

项目功能

  • 音乐播放
  • 用户登录注册
  • 用户信息编辑、头像修改
  • 歌曲、歌单搜索
  • 歌单打分
  • 歌单、歌曲评论
  • 歌单列表、歌手列表分页显示
  • 歌词同步显示
  • 音乐收藏、下载、拖动控制、音量控制
  • 后台对用户、歌曲、歌手、歌单信息的管理

技术栈

后端

SpringBoot + MyBatis + Redis

前端

Vue3.0 + TypeScript + Vue-Router + Vuex + Axios + ElementPlus + Echarts

开发环境

JDK: jdk-8u141

mysql:mysql-5.7.21-1-macos10.13-x86_64(或者更高版本)

redis:5.0.8

node:14.17.3

IDE:IntelliJ IDEA 2018、VSCode


项目展示

前台-首页-展示

前台-首页-代码

<template><el-container><el-header><yin-header></yin-header></el-header><el-main><router-view /><yin-current-play></yin-current-play><yin-play-bar></yin-play-bar><yin-scroll-top></yin-scroll-top><yin-audio></yin-audio></el-main><el-footer><yin-footer></yin-footer></el-footer></el-container>
</template><script lang="ts" setup>
import { getCurrentInstance } from "vue";
import YinHeader from "@/components/layouts/YinHeader.vue";
import YinCurrentPlay from "@/components/layouts/YinCurrentPlay.vue";
import YinPlayBar from "@/components/layouts/YinPlayBar.vue";
import YinScrollTop from "@/components/layouts/YinScrollTop.vue";
import YinFooter from "@/components/layouts/YinFooter.vue";
import YinAudio from "@/components/layouts/YinAudio.vue";const { proxy } = getCurrentInstance();if (sessionStorage.getItem("dataStore")) {proxy.$store.replaceState(Object.assign({}, proxy.$store.state, JSON.parse(sessionStorage.getItem("dataStore"))));
}window.addEventListener("beforeunload", () => {sessionStorage.setItem("dataStore", JSON.stringify(proxy.$store.state));
});
</script><style lang="scss" scoped>
@import "@/assets/css/var.scss";
@import "@/assets/css/global.scss";.el-container {min-height: calc(100% - 60px);
}
.el-header {padding: 0;
}
.el-main {padding-left: 0;padding-right: 0;
}
</style>

前台-歌单-展示

前台-歌单-代码

<template><div class="play-list-container"><yin-nav :styleList="songStyle" :activeName="activeName" @click="handleChangeView"></yin-nav><play-list :playList="data" path="song-sheet-detail"></play-list><el-paginationclass="pagination"backgroundlayout="total, prev, pager, next":current-page="currentPage":page-size="pageSize":total="allPlayList.length"@current-change="handleCurrentChange"></el-pagination></div>
</template><script lang="ts">
import { defineComponent, ref, computed } from "vue";
import YinNav from "@/components/layouts/YinNav.vue";
import PlayList from "@/components/PlayList.vue";
import { SONGSTYLE } from "@/enums";
import { HttpManager } from "@/api";export default defineComponent({components: {YinNav,PlayList,},setup() {const activeName = ref("全部歌单");const pageSize = ref(15); // 页数const currentPage = ref(1); // 当前页const songStyle = ref(SONGSTYLE); // 歌单导航栏类别const allPlayList = ref([]); // 歌单const data = computed(() => allPlayList.value.slice((currentPage.value - 1) * pageSize.value, currentPage.value * pageSize.value));// 获取全部歌单async function getSongList() {allPlayList.value = ((await HttpManager.getSongList()) as ResponseBody).data;currentPage.value = 1;}// 通过类别获取歌单async function getSongListOfStyle(style) {allPlayList.value = ((await HttpManager.getSongListOfStyle(style)) as ResponseBody).data;currentPage.value = 1;}try {getSongList();} catch (error) {console.error(error);}// 获取歌单async function handleChangeView(item) {activeName.value = item.name;allPlayList.value = [];try {if (item.name === "全部歌单") {await getSongList();} else {await getSongListOfStyle(item.name);}} catch (error) {console.error(error);}}// 获取当前页function handleCurrentChange(val) {currentPage.value = val;}return {activeName,songStyle,pageSize,currentPage,allPlayList,data,handleChangeView,handleCurrentChange,};},
});
</script>

前台-歌手-展示

前台-歌手-代码

<template><div class="play-list-container"><yin-nav :styleList="singerStyle" :activeName="activeName" @click="handleChangeView"></yin-nav><play-list :playList="data" path="singer-detail"></play-list><el-paginationclass="pagination"backgroundlayout="total, prev, pager, next":current-page="currentPage":page-size="pageSize":total="allPlayList.length"@current-change="handleCurrentChange"></el-pagination></div>
</template><script lang="ts" setup>
import { ref, computed } from "vue";
import YinNav from "@/components/layouts/YinNav.vue";
import PlayList from "@/components/PlayList.vue";
import { singerStyle } from "@/enums";
import { HttpManager } from "@/api";// data
const activeName = ref("全部歌手");
const pageSize = ref(15); // 页数
const currentPage = ref(1); // 当前页
const allPlayList = ref([]);
// computed
const data = computed(() => {return allPlayList.value.slice((currentPage.value - 1) * pageSize.value, currentPage.value * pageSize.value);
});// 获取所有歌手
async function getAllSinger() {const result = (await HttpManager.getAllSinger()) as ResponseBody;currentPage.value = 1;allPlayList.value = result.data;
}getAllSinger();// 获取当前页
function handleCurrentChange(val) {currentPage.value = val;
}function handleChangeView(item) {activeName.value = item.name;allPlayList.value = [];if (item.name === "全部歌手") {getAllSinger();} else {getSingerSex(item.type);}
}// 通过性别对歌手分类
async function getSingerSex(sex) {const result = (await HttpManager.getSingerOfSex(sex)) as ResponseBody;currentPage.value = 1;allPlayList.value = result.data;
}
</script>

前台-其他页面展示

后台-登录-展示

后台-登录-代码

<template><div class="login-container"><div class="title">{{ nusicName }}</div><div class="login"><el-form :model="ruleForm" :rules="rules"><el-form-item prop="username"><el-input v-model="ruleForm.username" placeholder="username"></el-input></el-form-item><el-form-item prop="password"><el-input type="password" placeholder="password" v-model="ruleForm.password" @keyup.enter="submitForm"></el-input></el-form-item><el-form-item><el-button class="login-btn" type="primary" @click="submitForm">登录</el-button></el-form-item></el-form></div></div>
</template><script lang="ts">
import { defineComponent, getCurrentInstance, ref, reactive } from "vue";
import mixin from "@/mixins/mixin";
import { HttpManager } from "@/api/index";
import { RouterName, MUSICNAME } from "@/enums";export default defineComponent({setup() {const { proxy } = getCurrentInstance();const { routerManager } = mixin();const nusicName = ref(MUSICNAME);const ruleForm = reactive({username: "admin",password: "123",});const rules = reactive({username: [{ required: true, message: "请输入用户名", trigger: "blur" }],password: [{ required: true, message: "请输入密码", trigger: "blur" }],});async function submitForm() {let username = ruleForm.username;let password = ruleForm.password;const result = (await HttpManager.getLoginStatus({username,password})) as ResponseBody;(proxy as any).$message({message: result.message,type: result.type,});if (result.success) routerManager(RouterName.Info, { path: RouterName.Info });}return {nusicName,ruleForm,rules,submitForm,};},
});
</script><style scoped>
.login-container {position: relative;background: url("../assets/images/background.jpg");background-attachment: fixed;background-position: center;background-size: cover;width: 100%;height: 100%;
}.title {position: absolute;top: 50%;width: 100%;margin-top: -230px;text-align: center;font-size: 30px;font-weight: 600;color: #fff;
}.login {position: absolute;left: 50%;top: 50%;width: 300px;margin: -150px 0 0 -190px;padding: 40px;border-radius: 5px;background: #fff;
}.login-btn {width: 100%;
}
</style>

后台-首页-展示

首台-首页-代码

<template><yin-header></yin-header><yin-aside></yin-aside><div class="content-box" :class="{ 'content-collapse': collapse }"><router-view></router-view></div><yin-audio></yin-audio>
</template><script lang="ts" setup>
import { ref } from "vue";
import YinHeader from "@/components/layouts/YinHeader.vue";
import YinAudio from "@/components/layouts/YinAudio.vue";
import YinAside from "@/components/layouts/YinAside.vue";
import emitter from "@/utils/emitter";const collapse = ref(false);
emitter.on("collapse", (msg) => {collapse.value = msg as boolean;
});
</script><style scoped>
.content-box {position: absolute;left: 150px;right: 0;top: 60px;bottom: 0;overflow-y: scroll;transition: left 0.3s ease-in-out;padding: 20px;
}.content-collapse {left: 65px;
}
</style>

后台-其他页面-展示

视频展示

音乐网站

详细步骤及代码

Pom.xml导包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.2</version><relativePath/></parent><artifactId>music-server</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><!--web启动 内嵌tomcat--><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions></dependency><!--引入测试--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--mysql-connector-java就是帮助java程序操作mysql的驱动程序。通过与mysql服务端建立连接,发送sql语句并且获取结果集--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.16</version></dependency><!--mybatis用来和数据库进行交互--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.4</version></dependency><!--这里面有点小工具啥的--><!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.8.1</version></dependency><!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.47</version></dependency><!-- Log4j --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j</artifactId><version>1.3.8.RELEASE</version></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-to-slf4j</artifactId><version>2.8.2</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.1</version></dependency><!-- 热部署模块 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!-- redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>2.6.6</version></dependency><!--<spring2.X集成redis所需common-pool2--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.11.1</version></dependency></dependencies><build><plugins><!-- 加上这段代码  打包插件 因为启动不起来 --><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>2.22.1</version><configuration><skipTests>true</skipTests></configuration></plugin></plugins></build></project>

1.启动类

package com.example.yin;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
@MapperScan("com.example.yin.mapper")
public class YinMusicApplication {public static void main(String[] args) {SpringApplication.run(YinMusicApplication.class, args);}}

2.过滤器

package com.example.yin.config;import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class CorsInterceptor extends HandlerInterceptorAdapter {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");response.setHeader("Access-Control-Max-Age", "3600");response.setHeader("Access-Control-Allow-Headers", "x_requested_with,x-requested-with,Authorization,Content-Type,token");response.setHeader("Access-Control-Allow-Credentials", "true");return true;}
}

3.Redis配置

package com.example.yin.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;import java.time.Duration;@EnableCaching //开启缓存注解
@Configuration
public class RedisConfig extends CachingConfigurerSupport {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();RedisSerializer<String> redisSerializer = new StringRedisSerializer();Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);template.setConnectionFactory(factory);//key序列化方式template.setKeySerializer(redisSerializer);//value序列化template.setValueSerializer(jackson2JsonRedisSerializer);//value hashmap序列化template.setHashValueSerializer(jackson2JsonRedisSerializer);return template;}@Beanpublic CacheManager cacheManager(RedisConnectionFactory factory) {RedisSerializer<String> redisSerializer = new StringRedisSerializer();Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);//解决查询缓存转换异常的问题ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);// 配置序列化(解决乱码的问题),过期时间600秒RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(600)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)).disableCachingNullValues();return RedisCacheManager.builder(factory).cacheDefaults(config).build();}
}

4.application.properties配置文件

mybatis.typeAliasesPackage=com.example.yin.model.domain
mybatis.mapperLocations=classpath:mapper/*.xml
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
server.port=8888
#热部署生效
spring.devtools.restart.enabled=false
#设置重启的目录
spring.devtools.restart.additional-paths=src/main/java
#classpath目录下的WEB-INF文件夹内容修改不重启
spring.devtools.restart.exclude=WEB-INF/**
# 关闭CONDITIONS EVALUATION REPORT及自动配置内容向控制台的输出
logging.level.org.springframework.boot.autoconfigure=ERROR
# redis相应的地址  还有一些配置
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database=0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0
spring.profiles.active=dev

5.log4j.properties配置文件

# LOG4J
log4j.rootCategory=INFO, stdout,file# print to console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yy-MM-dd HH:mm:ss,SSS} %5p %c{1}:%L - %m%n# print to file
log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.DatePattern='-'yyyy-MM-dd'.log'
log4j.appender.file.File=./logs/musicWebsite
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} %p [%c]: %m%n

源码获取

        ✨还可以关注宫纵号《编程乐学》,菜单栏有很多优质的开源项目以及更多的编程资料等你来学习。

这篇关于Java小案例-MusiQ音乐网站的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/531196

相关文章

Java中的String.valueOf()和toString()方法区别小结

《Java中的String.valueOf()和toString()方法区别小结》字符串操作是开发者日常编程任务中不可或缺的一部分,转换为字符串是一种常见需求,其中最常见的就是String.value... 目录String.valueOf()方法方法定义方法实现使用示例使用场景toString()方法方法

Java中List的contains()方法的使用小结

《Java中List的contains()方法的使用小结》List的contains()方法用于检查列表中是否包含指定的元素,借助equals()方法进行判断,下面就来介绍Java中List的c... 目录详细展开1. 方法签名2. 工作原理3. 使用示例4. 注意事项总结结论:List 的 contain

Java实现文件图片的预览和下载功能

《Java实现文件图片的预览和下载功能》这篇文章主要为大家详细介绍了如何使用Java实现文件图片的预览和下载功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... Java实现文件(图片)的预览和下载 @ApiOperation("访问文件") @GetMapping("

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

SpringCloud动态配置注解@RefreshScope与@Component的深度解析

《SpringCloud动态配置注解@RefreshScope与@Component的深度解析》在现代微服务架构中,动态配置管理是一个关键需求,本文将为大家介绍SpringCloud中相关的注解@Re... 目录引言1. @RefreshScope 的作用与原理1.1 什么是 @RefreshScope1.

Java并发编程必备之Synchronized关键字深入解析

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种... 目录一、前言二、Synchronized关键字2.1 Synchronized的特性1. 互斥2.

Spring Boot 配置文件之类型、加载顺序与最佳实践记录

《SpringBoot配置文件之类型、加载顺序与最佳实践记录》SpringBoot的配置文件是灵活且强大的工具,通过合理的配置管理,可以让应用开发和部署更加高效,无论是简单的属性配置,还是复杂... 目录Spring Boot 配置文件详解一、Spring Boot 配置文件类型1.1 applicatio

Java中StopWatch的使用示例详解

《Java中StopWatch的使用示例详解》stopWatch是org.springframework.util包下的一个工具类,使用它可直观的输出代码执行耗时,以及执行时间百分比,这篇文章主要介绍... 目录stopWatch 是org.springframework.util 包下的一个工具类,使用它

Java进行文件格式校验的方案详解

《Java进行文件格式校验的方案详解》这篇文章主要为大家详细介绍了Java中进行文件格式校验的相关方案,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、背景异常现象原因排查用户的无心之过二、解决方案Magandroidic Number判断主流检测库对比Tika的使用区分zip

Java实现时间与字符串互相转换详解

《Java实现时间与字符串互相转换详解》这篇文章主要为大家详细介绍了Java中实现时间与字符串互相转换的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、日期格式化为字符串(一)使用预定义格式(二)自定义格式二、字符串解析为日期(一)解析ISO格式字符串(二)解析自定义