В последнее время у меня немного чешутся руки и я думаю что делать.Я думал написать систему заказа песен КТВ.Смоделировал идею открытия счета КТВ.Я был измотан за 7 дней , но там довольно много технических моментов. Надеюсь, вы сможете это прочитать. (~^㉨^)~
Используйте Node (Express), чтобы научить вас писать систему заказа песен KTV, включая интерфейсную систему управления контентом и фоном, интегрировать платформу Express и разработку сервера базы данных Mongodb; научить вас писать супер красивые страницы с Vue.JS, ElementUI и iViewUI. , и заказывать песни по желанию слушать свое сердце
作者原创文章, 转载前请留言或联系作者!!!
карта разума
стек технологий
-
Бэкэнд: Express + Mongodb + jsonwebtoken и т. д.
-
Внешний интерфейс: Vue.JS + ElementUI + iViewUI + Axios и др.
Функции
Этот проект разделен на front-end разработку, back-end разработку и серверную разработку.
- Пользователям необходимо войти в систему, чтобы слушать песни (защита маршрутизации)
- Пользователям необходимо обратиться к администратору, чтобы подать заявку на получение учетной записи и пароля.
- Войдите в систему, чтобы слушать песни (песня в стиле, песня на языке, песня о звезде, популярная песня и т. д.)
- Напоминание с оставшимся временем 30 минут и автоматический выход из машины, когда время
- Добавления, удаления и изменения песен администратором
- Администратор открывает учетную запись для пользователя и может выбрать время доступа к компьютеру.
- Порядок просмотра администратором, порядок удаления, порядок поиска
- Администратор собирает песни и рекомендует их на ктв, чтобы рекомендовать песни
- так далее...
структура дизайна проекта
-- 服务器基本架构
ktv-select_music-system
├── README.md
├── index.js -- 后台文件入口
├── test.http -- 测试文件
├── api -- 路由文件
│ ├── admin.js -- 配置管理员的操作
| ├── music.js -- 配置歌曲信息
| ├── user.js -- 配置用户的相关操作
| └── safecode.js -- 配置安全码
├── config -- 配置
| ├── Date.js -- 配置日期格式化插件
| ├── delNoUse.js -- 封装闲置删除闲置资源方法
| ├── http.js -- 配置跨域
| ├── isBadAccount.js -- 封装账户是否合法
| ├── newaccount.js -- 封装随机开户方法
| ├── passport.js -- 验证token是否合法
| ├── uploadImg.js -- 封装上传图片方法
| └── uploadMusic.js -- 封装上传歌曲方法
├── ktv-admin --后台管理系统界面
├── ktv-client --前台用户点歌项目界面
├── dbModel
| └── ** -- Mongodb数据库的一些模型
├── mongodb
| └── mongodb.js -- 配置Mongodb,链接数据库
├── secret
| ├── mongodbURI.js -- Mongodb地址
| └── jwtkey.js -- token的私钥
├── static -- 资源存放处
| ├── music -- 歌曲上传目标文件夹
| ├── poster -- 歌曲海报上传目标文件夹
└── └── view -- 配置404文件
-- 后台管理系统架构
ktv-admin
├── README.md
├── public
| ├── index.html -- vue挂载页面
| └── ** -- 你可以在这里链接少量静态资源
├── src -- 开发文件夹
| ├── App.vue -- Vue挂载根页面
| ├── main.js -- Vue程序入口文件,挂载各种组件
| ├── router.js -- Vue路由配置文件
| ├── store.js -- Vuex的状态管理文件
| ├── assets -- 静态资源文件夹
| ├── components --公共组件
| | └── nav.vue -- 后台导航栏
| ├── plugins --插件
| | ├── axios.js -- 配置跨域,拦截器等等
| | ├── Date.js -- 格式化日期
| | └── Date.js -- 加载动画Loading
| ├── stores -- 状态管理文件夹
| | └── adminStore.js -- 管理员状态
| ├── views -- 页面文件夹
| | ├── 404.vue -- 404页面
| | ├── adminlikes.vue -- 管理员处理ktv收藏歌曲
| | ├── allorders.vue -- 订单管理
| | ├── Home.vue -- 后台根页面
| | ├── Index.vue -- 后台首页
| | ├── managemusic.vue -- 音乐管理
| | ├── user_service.vue -- 给用户开户
| | └── login.vue -- 后台登录
└── babel.config.js -- babel配置
-- 前台用户听歌架构
ktv-client
├── README.md
├── public
| ├── index.html -- vue挂载页面
| └── ** -- 你可以在这里链接少量静态资源
├── src -- 开发文件夹
| ├── App.vue -- Vue挂载根页面
| ├── main.js -- Vue程序入口文件,挂载各种组件
| ├── router.js -- Vue路由配置文件
| ├── store.js -- Vuex的状态管理文件
| ├── assets -- 静态资源文件夹
| ├── components --公共组件
| | ├── bottomNav.vue -- 底部音乐控制区域
| | └── topNav.vue -- 顶部信息区域
| ├── config --配置
| | ├── addSong.js --封装选取歌曲方法
| | ├── isBadAccount.js --验证账户合法性
| | ├── isLogin.js --是否登录
| | ├── nextSong.js --封装下一首歌曲方法
| | └── prevSong.js --封装上一首歌曲方法
| ├── plugins --插件
| | ├── axios.js -- 配置跨域,拦截器等等
| | └── wsmLoading.js -- 加载动画Loading
| ├── stores -- 状态管理文件夹
| | └── song.js -- 存储歌曲信息
| ├── views -- 页面文件夹
| | ├── 404.vue -- 404页面
| | ├── abc.vue -- 拼音点歌
| | ├── artist.vue -- 明星点歌
| | ├── Home.vue -- 后台根页面
| | ├── Index.vue -- 后台首页
| | ├── hot.vue -- 热播歌曲
| | ├── ktvlikes.vue -- ktv推荐歌曲
| | ├── selected.vue -- 已选歌曲
| | ├── style.vue -- 风格点歌
| | └── language.vue -- 语种点歌
├── babel.config.js -- babel配置
└── vue.config.js -- vue配置
Ознакомление с запуском проекта
первый
-
Не меняйте сначала порт сервера, иначе будет сообщено об ошибке.
-
Вам необходимо провести тестирование в среде с установленными Node и Vue, если у вас их нет, сначала загрузите (Загрузка узла,vue скачать).если нет
Mongodb
Базу данных, пожалуйста, скачайте сами, и я не буду много говорить об окружении. -
Сначала загрузите зависимости в самой внешней папке: npm install для загрузки внутренних зависимостей,
-
Затем введите ktv-client, npm install, чтобы загрузить пользовательские интерфейсные зависимости.
-
Затем введите ktv-admin, npm install, чтобы загрузить интерфейсные зависимости администратора.
-
После того, как вышеуказанная работа будет завершена, используйте команду
npm run server 或者 node index
Команда запускает сервер Node, и если запуск прошел успешно, он отобразит:Server is running on port [8633].
Mongodb is Connected.Please have a great coding.
-
Войдите в ktv-client, откройте командную панель и используйте команду
npm run client
Запустите пользовательский проект переднего плана и используйте браузер для доступа к нему после успешного запуска.http://localhost:xxxx
-
Войдите в ktv-admin, откройте командную панель, используйте команду
npm run admin
Запустите проект системы фонового управления и используйте браузер для доступа к нему после успешного запуска.http://localhost:xxxx
-
В этом примере Mongodb развернут на локальном компьютере.Если вы внимательно прочитаете этот документ, вам будет легко запустить проект. Если вы развертываете Mongodb в другом месте, измените его самостоятельно.
secret/mongodbURI.js
Информация профиля. -
Проект запущен успешно, лучше всего открыть его в браузере Chrome, украсить полосу прокрутки
Затем зарегистрируйте учетную запись администратора
admin.js最下面有个注册接口
// 管理员注册
router.post("/account/register", (req, res) => {
const email = req.body.email;
Admin.findOne({email})
.then(hasOne => {
if(hasOne){
return req.status(422).json({status:"422", result:"邮箱被占用"});
}else{
const username = req.body.username;
const password = req.body.password;
const identity = req.body.identity ? req.body.identity : null;
const date = new Date().format("yyyy/MM/dd HH:mm:ss");
const newAdmin = new Admin({
email,
username,
password,
identity,
date
});
newAdmin.save()
.then(() => {
res.json({status:"200", result:"注册成功"})
}).catch(err => {
console.log(err);
res.status(500).json({status:"500", result:"未知错误,注册失败"})
})
}
})
})
Затем зарегистрируйтесь в почтальоне или других инструментах.
Технические исследования
Метод даты
По причине того, что мозг не прост в использовании, а в js нет метода форматирования дат с элементами, буду дурить (стоит поучиться)
Date.js
/**
*
* @author: Mr_Wei
* @version: 1.0.0
* @description: 格式化日期
* @Date: 2019/10/16 09:32
*
*/
Date.prototype.format = function(format) {
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"H+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"f+": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(format))
format = format.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(format))
format = format.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return format;
}
export default Date.prototype.format
然后我们使用其格式日期
require(Date);
// const now = new Date().format("yyyy/MM/dd HH:mm:ss.S");
const now = new Date().format("yyyy/MM/dd HH:mm:ss");
Капча (svg-капча)
Использование проверочного кода svg-captcha предотвращает взлом пароля методом грубой силы и повышает безопасность. Подробный адрес документа:svg-captcha
Используйте код подтверждения
// 后台生成验证码
router.get("/getCaptcha", (req, res) => {
var captcha = svgCaptcha.create({
// 翻转颜色
inverse: false,
// 字体大小
fontSize: 38,
// 噪声线条数
noise: 3,
// 宽度
width: 80,
// 高度
height: 32,
});
// 保存到session,忽略大小写
req.session = captcha.text.toLowerCase();
console.log(req.session); //0xtg 生成的验证码
//保存到cookie 方便前端调用验证
res.cookie('captcha', req.session);
res.setHeader('Content-Type', 'image/svg+xml');
res.send(String(captcha.data));
res.end();
})
// 前台获取验证码
--HTML
<img width="80" style="background:#EEE9E9;margin-left:30px;" ref="captcha" height="32" src="http://localhost:3001/api/user/getCaptcha" @click="refreshCaptcha">
--js
// 获取验证码cookie
getCookie(cname){
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++){
var c = ca[i].trim();
if (c.indexOf(name)==0) return c.substring(name.length,c.length);
}
return "";
},
// 刷新验证码
refreshCaptcha(){
this.$refs.captcha.src = "http://localhost:3001/api/user/getCaptcha?d=" + Math.random();
},
最后用 填写的验证码进行对比
Загрузить песню или картинку
грозный для обработки информации о загрузке файлов, он удобен в использовании и очень дружелюбен.Если вы не сталкивались с файловыми операциями, поторопитесь и соберите его
Упаковать метод песни uploadMusic.js
/**
*
* @author: Mr_Wei
* @version: 1.0.0
* @description: 封装上传音乐方法
* @Date: 2019/10/16 08:35
*
*/
const fs = require('fs');
const path = require('path');
const formidable = require('formidable'); // 文件处理库
const formatTime = require('silly-datetime'); // 格式化数据
module.exports = (req, res) => {
let form = new formidable.IncomingForm(); //创建上传表单
form.encoding = 'utf-8'; // 设置编码格式
form.uploadDir = path.join(__dirname, '../static/music'); // 设置上传目录(这个目录必须先创建好)
form.keepExtensions = true; // 保留文件后缀名
form.maxFieldsSize = 20 * 1024 *1024; // 设置文件大小
/* 格式化form数据 */
form.parse(req, (err, fields, files) => {
let file = files.file;
/* 获取异常 */
if(err) {
return res.status(500).json({'status': 500, result: '服务器内部错误'});
}
if(file.size > form.maxFieldsSize) {
fs.unlink(file.path);
return res.status(412).json({'status': 412, result: '音频不能超过20M'});
}
/* 存储后缀名 */
let extName = '';
switch (file.type) {
case 'audio/mp3':
extName = 'mp3';
break;
}
if(extName.length == 0) {
fs.unlink(file.path);
return res.status(412).json({'status': 412, result: '只支持mp3格式音频'});
}
/* 拼接新的文件名 */
let time = formatTime.format(new Date(), 'YYYYMMDDHHmmss');
let num = Math.floor(Math.random() * 8999 + 10000);
let songName = `${time}_${num}.${extName}`;
let newPath = form.uploadDir + '/' + songName;
/* 更改名字和路径 */
fs.rename(file.path, newPath, (err) => {
if(err) {
return res.status(500).json({'status': 500, result: '音频上传失败'});
} else {
return res.send({'status': 200, 'msg': '音频上传成功', result: {src: songName}});
}
})
})
};
Vue, использование пейджинга ElementUI
Подробнее о подкачке ElementUI см.:Изучение разбиения на страницы ElementUI
Над
-- html
<el-pagination
v-if='paginations.total > 0'
:page-sizes="paginations.page_sizes"
:page-size="paginations.page_size"
:layout="paginations.layout"
:total="paginations.total"
:current-page.sync='paginations.page_index'
@current-change='handleCurrentChange'
@size-change='handleSizeChange'>
</el-pagination>
-- js
data(){
return{
allUsers:[], // 用来存储最终信息, 被显示的dom点调用
allTableData:[], // 用户承接分页设置的数据
paginations: { // 分页组件信息
page_index: 1, // 当前位于哪页
total: 0, // 总数
page_size: 5, // 1页显示多少条
page_sizes: [5, 10, 15, 20], //每页显示多少条
layout: "total, sizes, prev, pager, next, jumper" // 翻页属性
},
}
},
methods:{
// 获取当前页
handleCurrentChange(page) {
let sortnum = this.paginations.page_size * (page - 1);
let table = this.allTableData.filter((item, index) => {
return index >= sortnum;
});
// 设置默认分页数据
this.getAllUsers = table.filter((item, index) => {
return index < this.paginations.page_size;
});
this.getAllUsers = table.filter((item, index) => {
return index < this.paginations.page_size;
});
},
// 切换size
handleSizeChange(page_size) {
this.paginations.page_index = 1;
this.paginations.page_size = page_size;
this.getAllUsers = this.allTableData.filter((item, index) => {
return index < page_size;
});
},
// 总页数
setPaginations() {
this.paginations.total = this.allTableData.length;
this.paginations.page_index = 1;
this.paginations.page_size = 5;
// 设置默认分页数据
this.getAllUsers = this.allTableData.filter((item, index) => {
return index < this.paginations.page_size;
});
},
}
Она ушла? Да, пейджинг — это так просто! Вы научились этому? Некоторые студенты, изучающие фронтенд-разработку, никогда не были знакомы с пейджингом. Изучив это, вы больше не будете беспокоиться!
Законность токена и пользовательской проверки
jsonwebtoken — это токен, который шифрует информацию о пользователе в необратимо взломанный токен. Что касается паспорта-jwt, он используется для проверки истечения срока действия информации о токене, предоставленной по запросу пользователя. Если срок действия визы превышен, стойка регистрации будет предложено отправить сообщение о том, что токен недействителен. , предлагая пользователю повторно получить легальную информацию о токене, иначе он не сможет продолжать запрашивать зашифрованную информацию;
Применение
- passport-jwt
const key = require("../config/keys").KEYORSECRET;
const JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt;
var opts = {}
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = key;
module.exports = passport => {
passport.use(new JwtStrategy(opts, (jwt_payload, done) => {
UserInfo.findById(jwt_payload.id)
.then(user => {
if (user) {
return done(null, user);
} else {
return done(null, false);
// or you could create a new account
}
})
}));
}
// 设置token
// 规则
const rule = {
id:String(userinfo._id),
username:userinfo.username,
email:userinfo.email,
date:user.date,
signdate:userinfo.signdate,
signcount:userinfo.signcount,
avatar:userinfo.avatar,
phone:userinfo.phone
};
// 签证加密
// jwt.sign(规则, key(私钥), {配置:比如过期时长}, (err, token){ 响应程序 })
jwt.sign(rule,key,{expiresIn:7200},(err, token) => {
if(err) throw err;
res.json({"token" : "Bearer " + token})
})
自定义验证方法
/**
*
* @author: Mr_Wei
* @version: 1.0.0
* @description: 判断是否过期用户
* @Date: 2019/10/19 12:19
*
*/
const UserOrOrders = require("../dbModel/user");
module.exports = async params => {
const flag = await new Promise((resolve) => {
if(params){
const account = params.account;
UserOrOrders.findOne({account})
.then(user => {
if(user){
if(new Date().getTime() > new Date(user.endTime).getTime()){
console.log("过期用户");
// 处理
return resolve(false);
}else{
console.log("合法用户");
return resolve(true);
}
}else{
return resolve(false);
}
})
}else{
console.log("不合法用户");
return resolve(false);
}
})
return flag;
}
使用:
// 测试 isBadAccount(params)方法
router.post("/test", passport.authenticate("jwt", {session:false}), async (req, res) => {
// console.log(req.user)
if(await isBadAccount(req.user)){
// do something
res.send("OK");
}else{
res.status(401).json({status:"401", result:"帐号过期,请联系管理员"})
}
})
Подробный адрес документа:Юридическая проверка паспорта-Jwt,шифрование токена
снимок экрана
Система фонового управления
интерфейс песни на стойке регистрации
Исходный код здесь
Приведенный выше код был загружен на github.
GitHub.com/персиковая IC-карта/кондиционер…
Малая станция чтения
Портал:blog.US word.capable/views/pro JE…
отсканируй это: