大二课设,采用 bootstrap + express + mysql 实现电影售票系统(附带源码)

大二课程设计,前端采用了 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.csdn.net/download/Mr_changxin/20486492

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_CX_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值