音乐管理系统 SpringBoot + vue

文章目录

  • 1、简要介绍
  • 2、数据库设计
  • 3、解决的问题
    • 1、图片和音频的上传和存储
    • 2、分页功能
  • 4、数据返回


也算是进行了半个学期,跟着老师讲的进行

后端使用SpringBoot 前端 vue + layui
jdk 18

项目地址:gitee


1、简要介绍

  只有管理端,但是对用户端的判断功能在数据库中已经体现,

  实现了mp3音频文件和图片文件的上传,原理是存储在静态资源中,数据库中存的也是路径而已

以下为部分截图,整体风格亦是如此

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

2、数据库设计


CREATE TABLE `role`  (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '角色id',
  `role_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '角色名称',
  `role_status` int NULL DEFAULT NULL COMMENT '角色状态  0 不可用  1 可用',
  `create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  `dis` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `rule` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;


CREATE TABLE `singer`  (
  `id` int UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
  `sex` tinyint NULL DEFAULT NULL,
  `pic` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `birth` datetime NULL DEFAULT NULL,
  `location` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `introduction` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 44 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC;

CREATE TABLE `song`  (
  `id` int UNSIGNED NOT NULL AUTO_INCREMENT,
  `singer_id` int UNSIGNED NOT NULL,
  `name` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
  `introduction` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '发行时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `pic` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `lyric` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL,
  `url` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 124 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC;

CREATE TABLE `sys_user`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户名',
  `nick_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '昵称',
  `gender` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '性别',
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '密码  密文存储 ',
  `phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '手机号码',
  `status` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '1 可用   0 禁用',
  `image` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '服务器路径',
  `created_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '创建者',
  `role_uid` int NULL DEFAULT NULL COMMENT '角色  严格遵循外键约束,但在表设计时候,不使用约束语句',
  `create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

3、解决的问题

1、图片和音频的上传和存储

  首先肯定是要有一个对于存储文件的配置,然后,在前端发送的时候,注意使用
post + headers: { 'content-type': 'multipart/form-data' } 的格式,

package com.whd.system.controller;

import com.github.pagehelper.util.StringUtil;
import com.whd.system.common.AxiosResult;
import com.whd.system.mapper.SysUserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.Filter;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.util.UUID;

@RestController
@RequestMapping("/fileup")
public class FileUpController {

    public static final String UPLOAD_PATH = "F:\\javaweb\\study\\spingBootMaven\\src\\main\\resources\\upload\\";

    @PostMapping("/image")
    public AxiosResult<String> upload(@RequestParam(value = "name") String name,  MultipartFile photo, HttpServletRequest request) throws IOException {

        if(name==null || name.equals("")){
            return AxiosResult.error("参数错误");
        }

        String extension = StringUtils.getFilenameExtension(photo.getOriginalFilename());

        String fileName = UUID.randomUUID().toString().replaceAll("-", "") + "." + extension;

        String curDateStr = LocalDate.now().toString();


        File file = new File(UPLOAD_PATH+name+"\\"+ curDateStr);
        if (!file.exists()) {
            file.mkdirs();
        }

        photo.transferTo(new File(UPLOAD_PATH +name+ "\\" + curDateStr + "/" + fileName));

        return AxiosResult.success(name+ "\\" + curDateStr + "/" + fileName);
    }

    @PostMapping("/video")
    public AxiosResult<String> uploadViedo(@RequestParam(value = "name") String name,  MultipartFile video, HttpServletRequest request) throws IOException {

        name+="\\viedo";

        if(!video.getContentType().equals("audio/mpeg")) {
            return AxiosResult.error("只允许上传mp3格式的音频文件");
        }

        String extension = StringUtils.getFilenameExtension(video.getOriginalFilename());

        String fileName = UUID.randomUUID().toString().replaceAll("-", "") + "." + extension;

        String curDateStr = LocalDate.now().toString();
        System.out.println(UPLOAD_PATH+name+"\\"+ curDateStr);

        File file = new File(UPLOAD_PATH+name+"\\"+ curDateStr);
        if (!file.exists()) {
            file.mkdirs();
        }

        video.transferTo(new File(UPLOAD_PATH +name+ "\\" + curDateStr + "/" + fileName));

        return AxiosResult.success(name+ "\\" + curDateStr + "/" + fileName);
    }


}

2、分页功能

  通过自定义的一个组件(当然不是我写的),在后端进行判断处理,其实没有想到那么复杂

组件如下:

<script>
Vue.component('zpagenav', {
	template: `<nav class="zpagenav">` +
		`<ul class="page-ul">` +
		`<li v-bind:key="index" v-for="(item,index) in pageList" v-bind:class ="item.class" @click.stop="setPage(item)" v-html="item.html">` +
		`</li>` +
		`</ul>` +
		`<span class="total">共 {{total}} 条</span>` +
		`</nav>`,
	props: {
		prevHtml: String,
		nextHtml: String,
		page: Number,
		total: Number,
		pageSize: Number,
		maxPage: Number
	},
	computed: {
		pageList: function() {
			var _this = this,
				pageList = [];
			let pageCount = Math.ceil(_this.total / _this.pageSize);
			let page = _this.page;
			let prevHtml = _this.prevHtml ? _this.prevHtml : '&lt;';
			let nextHtml = _this.nextHtml ? _this.nextHtml : '&gt;';
			let maxPage = _this.maxPage ? _this.maxPage : 9;

			let hasPrev = page > 1;
			let hasNext = page < pageCount;

			//上一页
			pageList.push({
				class: hasPrev ? '' : 'disabled',
				page: hasPrev ? page - 1 : page,
				html: prevHtml
			});

			//首页
			pageList.push({
				class: page == 1 ? 'active' : '',
				page: 1,
				html: 1
			});

			var p0 = Math.floor(maxPage / 2);
			var p1 = 1 + 2 + p0; //首页+省略至少2个页码+中间页面数的一半

			var start, end;
			if(page >= p1) {
				start = page - p0;
				//前置省略号
				pageList.push({
					class: 'dot',
					page: page,
					html: '...'
				});
			} else {
				start = 2;
			}

			var p2 = page + p0;
			if(p2 < pageCount) {
				end = p2;
			} else {
				end = pageCount - 1;
			}

			//页码列表
			for(let i = start; i <= end; i++) {
				pageList.push({
					class: page == i ? 'active' : '',
					page: i,
					html: i
				});
			}

			if(end < pageCount - 1) {
				//后置省略号
				pageList.push({
					class: 'dot',
					page: page,
					html: '...'
				});
			}

			//尾页
			if(pageCount > 1) {
				pageList.push({
					class: page == pageCount ? 'active' : '',
					page: pageCount,
					html: pageCount
				});
			}

			//下一页
			pageList.push({
				class: hasNext ? '' : 'disabled',
				page: hasNext ? page + 1 : page,
				html: nextHtml
			});

			return pageList;
		}
	},
	methods: {
		setPage: function(item) {
			
			if(item.class == '') {
				this.$emit('pagehandler', item.page);
			}
		}
	}
});

</script>

<style>
.zpagenav {
	text-align: center;
	-webkit-user-select: none;
}

.zpagenav {
	font-family: arial;
	color: #48576a;
}

.zpagenav ul {
	display: inline-block;
	margin: 20px 20px;
	padding: 0;
}

.zpagenav ul li {
	display: inline-block;
	margin: 0;
	padding: 0 4px;
	border: 1px solid #d1dbe5;
	border-right: 0;
	background: #fff;
	font-size: 13px;
	min-width: 28px;
	height: 28px;
	line-height: 28px;
	cursor: pointer;
	box-sizing: border-box;
	text-align: center;
}

.zpagenav ul li:last-child {
	border-right: 1px solid #d1dbe5;
}

.zpagenav ul li:hover {
	color: #20a0ff;
}

.zpagenav ul li.active {
	border-color: #20a0ff;
	background-color: #20a0ff;
	color: #fff;
	cursor: default;
}

.zpagenav ul li.active:hover {
	color: #fff;
}

.zpagenav ul li.disabled {
	cursor: not-allowed;
	color: #e4e4e4;
}

.zpagenav ul li.dot {
	cursor: default;
}

</style>


使用的时候,注意引入该文件,和该文件的样式文件,然后,将这一坨复制到分页的地方

      <div class="page">
        <div class="wrap">
          <zpagenav v-bind:page="page" v-bind:page-size="pageSize" v-bind:total="total" v-bind:max-page="maxPage"
            v-on:pagehandler="pageHandler">
          </zpagenav>
        </div>
      </div>

注意,在vue 的 data 中加上 page: 1, pageSize: 10, total: 0, maxPage: 5,这三个属性,
然后,就是后端处理返回数据了,正常查询结果,通过 PageHelper PageInfo进行分页处理

处理逻辑如下例子:

 @PostMapping("/select/selectAllPage")
    public AxiosResult<PageResult<SongVo>> getAllSongByPage(@RequestBody PageVO pageVO){


        Integer page = pageVO.getPage();
        Integer size = pageVO.getSize();

        // 开始分页
        PageHelper.startPage(page, size);
        List<SongVo> songs=songMapper.getSongList();

        songs.forEach(song -> {
            song.setPic(PIC_URL + song.getPic());
            song.setUrl(PIC_URL + song.getUrl());
        });

        PageInfo<SongVo> pageInfo = new PageInfo<>(songs);

        //将数据封装在自己写的PageResult
        PageResult<SongVo> pageResult = new PageResult<>();
        pageResult.setPage(pageInfo.getPageNum());
        pageResult.setTotalPage(pageInfo.getPages());
        pageResult.setList(pageInfo.getList());
        pageResult.setTotal(pageInfo.getTotal());
        pageResult.setPageSize(pageInfo.getPageSize());

        return AxiosResult.success(pageResult);
    }

对于AxiosResult也是自己写的一个实体类, 用于返回信息和状态码, 具体代码可查看项目中的代码:
对于PageResult 类

@Data
public class PageResult<T> {
    private int page;
    private int pageSize;
    private long total;
    private List<T> list;
    private int totalPage;
}

PageVO 类

@Data
public class PageVO {
    private Integer page=1;//用户查询的第几页
    private Integer size=10;//每页展示的数量
}

4、数据返回

  通过SpringBoot返回的数据类型,会自动转为json格式,但是上面所提到的AxiosResult也不是必须的,但是这样子看起来更方便,该类可以作为模板重复使用

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

超多细节—app图标拖动排序实现详解

前言&#xff1a; 最近做了个活动需求大致类似于一个拼图游戏&#xff0c;非常接近于咱们日常app拖动排序的场景。所以想着好好梳理一下&#xff0c;改造改造干脆在此基础上来写一篇实现app拖动排序的文章&#xff0c;跟大家分享下这个大家每天都要接触的场景&#xff0c;到底…

Golang并发控制的三种方案

Channel Channel是Go在语言层面提供的一种协程间的通信方式&#xff0c;我们可以通过在协程中向管道写入数据和在待等待的协程中读取对应协程的次数来实现并发控制。 func main() {intChan : make(chan int, 5)waitCount : 5for i : 0; i < waitCount; i {go func() {intC…

ISCC2024 WriteUp

msic Funzip Funzip writeup解题思路 1.打开题目发现是一个base64 2.看了一遍后发现他不是很全于是写一个脚本进行补全 wf open("5.txt", "w") with open("1 (2).txt", "r") as f: data f.read() data data.splitlines() for l…

【AI+多智能体框架】个人整理的几款AI多智能体框架

昨天无意间了解到 alipay开源 的多智能体框架agentUniverse &#xff0c;这里聊一下。现在这个信息社会&#xff0c;讲究多角色协同工作。人工智能时代&#xff0c;多智能体协同工作也是大势所趋&#xff0c;虽然现在框架或多或少还存在瑕疵。 但所有新技术都是在发展中逐步迭代…

vue引入aos.js实现滚动动画

aos.js官方网站&#xff1a;http://michalsnik.github.io/aos/ aos.js介绍 AOS (Animate on Scroll) 是一个轻量级的JavaScript库&#xff0c;用于实现当页面元素随着用户滚动进入可视区域时触发动画效果。它不需要依赖 jQuery&#xff0c;可以很容易地与各种Web开发框架&#…

掌握rpc、grpc并探究内在本质

文章目录 rpc是什么&#xff1f;又如何实现服务通信&#xff1f;理解rpcRPC的通信过程通信协议的选择小结RPC VS Restful net_rpc实践案例net/rpc包介绍创建服务端创建client 看看net_rpc的通信调度实现的内部原理明确目标基于自己实现的角度分析我会怎么做代码分析 grpc介绍与…

QT修改界面图标及exe程序图标

目录 步骤1. 添加图标文件2. 添加保存图标变量3. 窗口启动初始化图标文件4. 构建后即可完成图标的更改 步骤 1. 添加图标文件 如下&#xff0c;添加一个名为 favicon.ico 的文件到.pro 工程文件所在的目录中。 2. 添加保存图标变量 RC_ICONS是一个变量&#xff0c;它被用于存储…

MaxKB-无需代码,30分钟创建基于大语言模型的本地知识库问答系统

简介 MaxKB 是一个基于大语言模型 (LLM) 的智能知识库问答系统。它能够帮助企业高效地管理知识&#xff0c;并提供智能问答功能。想象一下&#xff0c;你有一个虚拟助手&#xff0c;可以回答各种关于公司内部知识的问题&#xff0c;无论是政策、流程&#xff0c;还是技术文档&a…

老杨说运维 | 如何结合现状进行运维路径建设(文末附演讲视频)

青城山脚下的滔滔江水奔涌而过&#xff0c;承载着擎创一往无前的势头&#xff0c;共同去向未来。2024年6月&#xff0c;双态IT成都用户大会擎创科技“数智化可观测赋能双态运维”专场迎来了完满的收尾。 本期回顾来自擎创科技CTO葛晓波的现场演讲&#xff1a;数智化转型的核心目…

Ps:脚本与动作

有三种脚本语言可用于编写 Photoshop 脚本&#xff1a;AppleScript&#xff08;macOS&#xff09;、JavaScript 和 VBScript&#xff08;Windows&#xff09;。 Photoshop 脚本文件默认文件夹 Win&#xff1a;C:\Program Files\Adobe\Adobe Photoshop 2024\Presets\Scripts Mac…

lombok不起作用排查

1.idea中lombok插件已安装并启用 2.idea中annotation processors已勾选 3.项目中gradle或maven已引入lombok依赖 但提示还是找不到get,set方法。 还需要启用annotationProcessor 重点是annotationProcessor的配置&#xff0c;没有配置这个才是问题出现的关键&#xff01;&…

【机器学习300问】124、什么是LSTM?LSTM的基本结构是怎样的?

长短期记忆网络&#xff08;LSTM&#xff09;是一种解决隐变量模型长期信息保存和短期输入缺失问题的方法&#xff0c;有趣的是&#xff0c;长短期记忆网络的设计比门控循环单元稍微复杂一些&#xff0c; 却比门控循环单元早诞生了近20年。 一、什么是LSTM&#xff1f; LSMT全…

Character Animator 2024 mac/win版:赋予角色生命,动画更传神

Character Animator 2024是一款强大的角色动画制作软件&#xff0c;以其创新的功能和卓越的性能&#xff0c;为动画师、游戏开发者以及设计师们带来了全新的创作体验。 Character Animator 2024 mac/win版获取 这款软件采用了先进的骨骼绑定技术&#xff0c;使得角色动画的制作…

图解ZGC

ZGC&#xff08;Z Garbage Collector&#xff09; 是一款性能比 G1 更加优秀的垃圾收集器。ZGC 第一次出现是在 JDK 11 中以实验性的特性引入&#xff0c;这也是 JDK 11 中最大的亮点。在 JDK 15 中 ZGC 不再是实验功能&#xff0c;可以正式投入生产使用了&#xff0c;使用 –X…

corona渲染器与vray比哪个好?支持云渲染平台吗

​在视觉渲染技术领域&#xff0c;V-Ray和Corona都以其卓越的性能和广泛应用赢得了高度评价。这两款渲染器各有其独特的优势&#xff0c;使得在它们之间做出选择并非易事。不同的应用场景和用户需求可能会让它们各自展现出不同的优势。 一、corona渲染器跟vray怎么样 在比较V-…

【VUE3学习手札】

VUE3学习手札 vue3成长之路学习笔记 文章目录 VUE3学习手札前言一、markRaw1.1 代码示例1.2 应用场景1.3 拓展&#xff08;toRaw&#xff09;1.4 实际应用 二、ref 和 reactive 前言 主要用于自己的一个备忘&#xff0c;对知识点的查缺补漏 一、markRaw 将一个对象标记为不可被…

【启明智显技术分享】工业级HMI芯片-Model系列关于SDCard / Udisk 烧录时显示进度条和数字百分比技术指导

【Model系列芯片】 是启明智显针对工业、行业以及车载产品市场推出的系列HMI芯片&#xff0c;主要应用于工业自动化、智能终端HMI、车载仪表盘、两轮车彩屏仪表、串口屏、智能中控、智能家居、充电桩显示屏、储能显示屏、工业触摸屏等领域。此系列具有高性能、低成本的特点&am…

01studio的miropython哥伦布407开发板真实管脚

抹黑的管脚都不能用&#xff01;绿色是sd卡占用&#xff0c;红色的是串口占用&#xff0c;还有其他的只能看原理图了 管脚都引出来了&#xff0c;不能用&#xff0c;引出来干嘛呢&#xff1f;纯瞎耽误功夫 谨慎使用吧。

C++ 62 之 冒泡排序

#include <iostream> // #include <string> #include <cstring>using namespace std;// 冒泡排序:函数模板 template<typename T> void mySort(T arr[], int len){ // size参数是数组的个数&#xff0c;一定是int型的for (size_t i 0; i < len -1;…

玩转编程的终极挑战,C++究竟有多难?

C是一门非常强大和灵活的编程语言&#xff0c;它可以实现面向对象、泛型、元编程等多种编程范式&#xff0c;可以开发高性能的系统软件、游戏、图形、网络等各种应用。但是&#xff0c;C也是一门非常复杂和难学的语言&#xff0c;很多初学者在学习C的过程中会遇到很多困难和挫折…