文章目录
大二课程设计,前端采用了 bootstrap 框架,完成响应式布局,后端用了node平台中的 express 框架,在完成此项目的过程中,虽然遇到了许多问题,但是通过各种途径也一一解决了,在此将此项目以及完成过程的心得分享给大家,希望能给大家带来帮助。本人也是一位正在学习中的程序员,如果遇到什么问题或者建议,也希望大家能够指出。
前端
1. 主页模块
首先此系统参考了其他各大型影院的网上售票系统,再经过小组自身实力的权衡等等,最终达到了如下的效果
抬头是一个巨幕,可以通过点击进行切换,也能自动变换,下面就是正在热映的电影的一个轮播图,用户能够通过点击相关电影图片进入到电影的详细介绍中。
电影详细介绍
通过电影详细介绍能让用户更好的了解这个电影信息,并且能在此界面进行购票操作。
主页的下方是当下比较热门电影的简介,同样,也能点击进去查看详细信息
主页的大题思路就完成了,具体还有许多细节就不在此一一展示了
2. 用户管理模块
展示完主页过后,要完成购票我们首先想到的就是要完成用户的管理,用户首先要拥有自己的账号才能进行买票,才能更好的方便管理。于是就有了售票管理。我们可以通过主页面中的登录按钮来进入登录页面。
注册页面
忘记密码页面
这些页面也都能够完成相应的功能,包括发送邮箱等等,主要采用了 Ajax 从后端接收数据,调用 node 发送邮箱的 API 来完成,整体来讲这部分并不是很难,前端的布局这些都能在一天之内完成。主要考虑的就是后端处理事务的能力、并发度、异步问题等等。此系统运用的是 mysql 数据库,用户数据都存储在数据库中,后期还有用户订单等其他数据也都存储在数据库中。
3. 用户个人中心页面
当我们登录成功以后,之前的登录页面右上角的登录按钮会变成我们用户的个人中心按钮(参考很多网页也是这样来实现的 ),其中的内容也会变成用户的昵称
点击进入个人主页
这就是目前个人主页已经完成的功能,能够看到自己的售票订单,以及对个人账户的管理,退出登录等等。
个人订单
修改密码
用户个人页面目前来讲由于时间有限就只完成了这些功能,后期如果有时间会继续将其完善。但是基本的雏形已经搭建完成。
4. 管理员页面
有了面向用户的应用程序,就有面向管理员的界面,管理员能通过此页面做出更高权限的操作。
管理员登录界面
登录成功后的页面
添加用户或者修改用户
查看电影售票历史
拥有较高权限的管理员界面能够完成许多普通用户完成不了的操作,这里也不一一展示了,如果有疑问,欢迎在评论区指出。
前端几大模块主要页面基本展示完成,还有很多页面就不向大家献丑啦,下面给大家展示下后端的事物处理
后端
1. 数据库处理
const mysql = require('mysql');
//创建数据池
//如果每次都是用createConnect来连接那么每次还需要进行关闭数据库连接的操作,略显繁琐
const pool = mysql.createPool({
connectionLimit: 10,
host: 'localhost', //默认情况下的主机名
user: 'root', //默认情况下的用户名
password: '111', //安装时设置的密码
database: 'ticketing', //连接的数据库名字
multipleStatements: true,
timezone: "SYSTEM"
});
//使用时传入对应参数,sql为相应sql语句,data为插入或修改所需要的数据
module.exports = (sql, data = []) => {
//使用Promise解决mysql的命令处理异步问题
return new Promise((resolve, reject) => {
//在数据池中进行操作
pool.query(sql, data, function (error, results, fields) {
if (error) {
reject(error.message)
} else {
resolve(results)
}
})
})
}
这里使用了 ES6 重点 promise 关键字,来处理前端发送请求可能带来的异步问题,以及连接 mysql 数据库。创建数据库常量池,减少频繁创建连接带来的资源浪费。将此模块封装起来,其他地方要使用时直接引入就行
2. 后台自动发送验证邮箱
const nodemailer = require("nodemailer");
// 创建一个 smtp 服务器
const config = {
secureConnection:true,
host:'smtp.163.com',
service:"163", // 邮箱
secure:true,
port:456,
auth:{
user:'ticketsystem2021@163.com',
pass:'IQQZDPQUYWEZJRYU' // 邮箱授权码
}
}
const transporter = nodemailer.createTransport(config);
module.exports = function(mail,res){
transporter.sendMail(mail,function(err,info){
if(err){
console.log(err);
res.json({status:400,msg:"send fail....."})
}else{
console.log(info);
res.json({status:200,msg:"邮件发送成功....."})
}
})
}
这里运用的是 node 中的 nodemailer 包来达到需求,创建一个163邮箱和 smtp 服务器
3. 用于产生六位随机验证码
module.exports = {
createSixNum:function (){
var Num="";
for(var i=0;i<6;i++)
{
Num+=Math.floor(Math.random()*10);
}
return Num;
}
}
接下来就是最重要的路由设置,返回请求给前端
4. 管理员路由设置
const express = require('express');
const db = require('./db.js')
const router = express.Router();
// 获取 post 请求
router.use(express.json())
router.use(express.urlencoded({ extended: false }));
// 修改默认路径
// 返回登陆界面
router.get('/admir',(req,res)=>{
console.log('/admir 管理员登陆');
res.render('sign-in.html');
})
/**
* @param user 用户名
* @param psw 密码
*
* ajax 判断账号密码否正确
*
* @returns yes 账号密码正确
* @returns no 账号密码错误
*/
router.get('/checkMan',async (req,res)=>{
const data = req.query;
console.log("/checkMan ajax 验证管理员账号密码")
const sql = 'select * from manage where user = ? and psw = ?';
try{
const result = await db(sql,[data.user,data.psw]);
if(result.length == 0){
res.send('no');
}else{
res.send('yes')
}
}catch(err){
// 数据库连接问题
res.render('403.html');
console.log(err);
}
})
// 通过 ajax 如果验证成功,则将管理员主页返回
router.post('/zhu_ye',async (req,res)=>{
const sql_users = "select * from user limit 5";
const sql_history = "select * from history limit 5"
const data_users = await db(sql_users);
const data_history = await db(sql_history);
let history = JSON.stringify(data_history);
let users = JSON.stringify(data_users);
console.log("查找到的用户数据为:"+users);
console.log('历史数据为:'+history)
users = JSON.parse(users);
history = JSON.parse(history)
res.render('zhu_ye.html',{
users:users,
history:history,
});
})
// 用户列表
router.post("/user_list",async (req,res)=>{
const sql = 'select * from user'
const data = await db(sql);
let data_str = JSON.stringify(data);
let user_list = JSON.parse(data_str);
res.render('users.html',{
users:user_list
})
})
// 用户删改
router.post('/user_change',(req,res)=>{
res.render('user_change.html');
})
// 其他
router.post('/more',(req,res)=>{
res.render('more.html');
})
// 电影售票统计
router.post('/total',async (req,res)=>{
const sql_history = 'select * from history';
let data = await db(sql_history);
data = JSON.stringify(data);
console.log('查询到的历史购票记录为:'+ data);
data = JSON.parse(data);
res.render('tong_ji.html',{
history:data,
});
})
// 电影上架
router.post("/shang_jia",(req,res)=>{
res.render('shang_jia.html');
})
// 用户名修改
router.post('/change_email',(req,res)=>{
const email = req.body.email;
console.log('需要修改用户信息的账号为:'+email)
res.render('user_change.html',{
email:email,
})
})
// 修改用户信息
router.get('/change_email',async (req,res)=>{
let data = req.query;
const sql_del = 'delete from user where user = ?';
const sql_ins = 'insert into user value(?,?,?)';
try{
let del_result = await db(sql_del,[data.user]);
let ins_result = await db(sql_ins,[data.user,data.psw,data.name])
res.render('man_change_success.html')
}catch(err){
res.send(err);
}
})
// 删除用户
router.post('/del_email',async (req,res)=>{
let data = req.body;
const sql_del = 'delete from user where user = ?';
try{
await db(sql_del,[data.email]);
res.redirect(307,'/user_list')
}catch(err){
res.send(err)
}
})
// 添加用户
router.post('/add_user',async (req,res)=>{
let data = req.body;
const sql_del = 'delete from user where user = ?';
const sql_ins = 'insert into user value(?,?,?)';
try{
let del_result = await db(sql_del,[data.user]);
let ins_result = await db(sql_ins,[data.user,data.psw,data.name])
res.render('man_change_success.html')
}catch(err){
res.send(err);
}
})
// 错误提示页面
router.post('/403',(req,res)=>{
res.render('403.html')
})
router.post('/404',(req,res)=>{
res.render('404.html')
})
router.post('/500',(req,res)=>{
res.render('500.html')
})
router.post('/503',(req,res)=>{
res.render('503.html')
})
// 创建路由
module.exports = router;
5. 用户路由设置
const express = require('express');
const db = require('./db.js')
const router = express.Router();
const tools = require('./tools.js')
const nodemail = require('./nodemail.js')
const moment = require('moment')
router.use(express.json())
router.use(express.urlencoded({ extended: false }));
router.get('/', function (req, res) {
console.log("/ 请求主页面")
res.render('menu.html', { url: '/user_login', btn_msg: '登录',vaule:'' });
})
// 响应登录页面
router.get('/user_login', function (req, res) {
console.log('/user_login 登录页面')
res.render('index.html');
})
// 响应忘记密码页面
router.get('/forgot', function (req, res) {
console.log('/forgot 忘记密码页面')
res.render('forgot.html');
})
// 响应注册页面
router.get('/sign-up', function (req, res) {
console.log('/sign-up 注册页面')
res.render('sign-up.html');
})
// 接收用户登录
/**
* data.user 用户名
* data.psw 密码
*
*/
router.post('/form', async (req, res) => {
// 获取 post 表单数据
const data = req.body;
console.log('/form 登录验证账号:'+data.user+'验证的密码为:'+ data.psw)
const sql = "select * from user where user = ? and psw = ?";
const result = await db(sql, [data.user, data.psw]);
let dataString = JSON.stringify(result);
dataString = JSON.parse(dataString)
if (result.length != 0) {
console.log(dataString)
res.render('menu.html', {
url: "/home?email="+data.user,
btn_msg: dataString[0].name,
sell:"email="+data.user
})
}
})
// 用于判断账号和密码是否正确
router.get('/formTest', async (req, res) => {
// 获取 get 数据
const data = req.query;
console.log('/formTest 验证的账号为'+data.user+",密码为:"+data.psw)
const sql = "select * from user where user = ? and psw = ?";
// 查询到的结果
const result = await db(sql, [data.user, data.psw]);
if (result.length == 0) res.send("no");
else res.send("yes");
})
// 购票页面
router.get('/sell_ticket1',(req,res)=>{
let data = req.query;
if(data.email){
res.render("sell_ticket1.html",{
sell_btn:data.email,
sell_email:"email="+data.email,
sell_url:"#",
})
}else{
res.render('sell_ticket1.html',{
sell_btn:'登录',
sell_url:'/user_login',
})
}
})
router.get('/sell_ticket2',(req,res)=>{
let data = req.query;
if(data.email){
res.render("sell_ticket2.html",{
sell_btn:data.email,
sell_url:"#",
})
}else{
res.render('sell_ticket2.html',{
sell_btn:'登录',
sell_url:'/user_login',
})
}
})
// 购票成功
router.get('/shop_success1',async (req,res)=>{
let email = req.query.email;
let date = moment(new Date()).format('YYYY-MM-DD HH:mm:ss')
console.log("当前时间为:"+date)
const sell_sql = "insert into history value(?,?,?,?)"
try{
await db(sell_sql,[email,'八月未央',60,date])
res.render('shop_success.html',{
email:email
})
}catch(err){
res.send(err)
}
})
router.get('/shop_success2',async (req,res)=>{
let email = req.query.email;
let date = moment(new Date()).format('YYYY-MM-DD HH:mm:ss')
console.log("当前时间为:"+date)
const sell_sql = "insert into history value(?,?,?,?)"
try{
await db(sell_sql,[email,'超越',60,date])
res.render('shop_success.html',{
email:email
})
}catch(err){
res.send(err)
}
})
// 接收用户注册
/**
* url /sign-up/from
*
*/
router.post('/sign-up/from', async (req, res) => {
// 使用 await promise 时会自动转换为 resolve 函数中的参数
const data = req.body;
console.log('/sign-up/form 注册的账号为'+data.user+",注册密码为:"+data.psw,"用户名为:"+data.name)
const sql = "insert into user value(?,?,?);";
const result = await db(sql, [data.user, data.psw,data.name]);
if (result.affectedRows == 0) res.render('sign_res.html', { result: "注册失败" });
else res.render('sign_res.html', { result: '注册成功' });
})
// 判断用户名是否重复 ajax
router.get('/findUser', async (req, res) => {
const data = req.query;
console.log("/findUser 判断 "+data.email+" 是否重复");
const sql = "select * from user where user = ?"
const result = await db(sql, [data.email]);
if (result.length == 0) res.send('yes');
else res.send('no');
})
// 发送邮件 ajax
router.get('/email', async (req, res) => {
const email = req.query.email;//刚刚从前台传过来的邮箱
const code = tools.createSixNum();//生成的随机六位数
const delay = 300000; // 验证码的作用时间
const sql = "select * from user where user = ?"
try {
const result = await db(sql, [email]);
console.log(result);
if (result.length == 0) {
res.json({ status: 400, message: 'no' });
} else {
req.body = { success: true, message: "账号可行" };//数据传回前台
var mail = {
// 发件人
from: 'ticketsystem2021@163.com',
// 主题
subject: '票多多 账号验证凭证',//邮箱主题
// 收件人
to: email,//前台传过来的邮箱
// 邮件内容,HTML格式
text: `您的验证码为${code} 票多多电影城是四川省电影公司全资影城、属拼多多电影院线旗下影院,
创立于2021年5月,距今已2个月历史,累计票房收入2.3亿元,接待观众超过2千余万。
影城成立以来先后投资三千余万元,经数次装修改造,使影城始终引领电影时尚潮流。
地处最繁华的春熙路商圈核心位置, 拥有18个豪华电影厅,观众座席数2000多座,
是全国影厅最多、节目最多、场次最多、人次最多的影城。率先引进数字3D电影,
影厅内安装有世界顶级的英国杜比CP650(EX)数字处理器、 美国JBL音响、德国ISCO一体化镜头、
美国QSC数字功放(DCA)、 6.1声道杜比数码立体声系统!发送者:ticketsystem2021@qq.com`
};
let time;
const delSql = "delete from checkcode where email = ?;"
const inSql = "insert into checkcode value(?,?);"
await db(delSql, [email]);
await db(inSql, [email, code]);
time = setTimeout(() => {
const delCode = "delete from checkcode where email = ?";
try {
db(delCode, [email]);
} catch (err) {
console.log(err);
}
}, delay)
nodemail(mail, res);//发送邮件
clearTimeout(time)
console.log("/email 发送邮件 email:" + email, "code:" + code)
}
} catch (err) {
console.log(err)
}
})
// 判断是否输入的邮箱与验证码是否符合 ajax
router.get('/findPsw', async (req, res) => {
const data = req.query;
console.log(data.email, data.code)
const sql = "select * from checkcode where email = ? and code = ?"
const result = await db(sql, [data.email, data.code]);
if (result.length > 0) {
console.log(result)
res.send("yes")
}
else res.send("no");
})
// 如果符合就跳转到修改密码界面
// 将邮箱传回
router.post('/findPsw', async (req, res) => {
console.log(req.body);
res.render('findPsw.html', {
email: req.body.email,
});
})
// 修改密码
router.post('/change', async (req, res) => {
console.log('/change 修改的email与新密码:' + req.body.email, req.body.newPsw)
const email = req.body.email;
const newPsw = req.body.newPsw;
const sql = "update user set psw = ? where user = ?"
const result = await db(sql, [newPsw, email]);
if (result.affectedRows == 0) {
res.send('服务器连接问题,修改失败');
} else {
res.render('change_success.html');
}
})
// 进入个人主页
router.get('/home',(req,res)=>{
const data = req.query;
res.render('home.html',{
email:data.email,
})
})
// 返回到菜单
router.get('/form',(req,res)=>{
const email = req.query.email;
res.render('menu.html', {
url: "/home",
btn_msg: email
})
})
// 用户修改密码
router.get('/changePsw',(req,res)=>{
const data = req.query.email;
console.log(data)
res.render('changePsw.html',{
email:data,
})
})
// 获取用户信息表单
router.post('/get_form',async (req,res)=>{
const email = req.body.email;
const sql = 'select * from user where user = ?'
let data = await db(sql,[email]);
data = JSON.stringify(data);
console.log('/get_form'+data);
console.log('/get_form:'+email);
res.send(data)
})
// 用户获取购票历史
router.get('/order',async (req,res)=>{
let email = req.query.email;
const sql_his = 'select * from history where email = ?'
try{
let result = await db(sql_his,[email])
result = JSON.stringify(result);
console.log('/order :'+result);
res.render('order.html',{
email:email,
history:JSON.parse(result)
})
}catch(err){
res.send(err);
}
})
module.exports = router;
6. 程序入口
const express = require('express');
const router_user = require('./source/router_user')
const router_ma = require("./source/router_ma")
const app = express();
// 引入路由
app.use(router_user)
app.use(router_ma)
// express 导入 art-template
app.set('views', [__dirname + '/views/user', __dirname + '/views/manage']);
app.engine('html',require('express-art-template'))
app.use('/public/',express.static('./public/'))
app.listen(3000,function(){
console.log('server is running.....');
})
7. 服务器在运行时会记录相应的日志信息
此次项目就展示到这里,可能在很多大佬面前这都不算什么,但是这是我一行一行敲出来的成果,在这个过程中,我学习到了很多东西,也希望能和大家一起学习,一起进步。作为一个上山的人,要多多学习,加倍努力才能登上高峰。
8. 下载地址
原创不易下载地址:https://download.youkuaiyun.com/download/Mr_changxin/20486492