本文主要是介绍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音乐网站的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!