nodejs + mongodb开发一套影视电影cms系统

在我做这个事情之前,前几年玩了好几年的cmp4,也不知道国内程序员有多少记得cmp晨风音乐播放器的,如果有,那就是我的同行了,在老早之前玩cmp播放器玩了几年,做音乐,做视频。 之前在社区看到过nodejs搞博客系统的很多,后来nodejs切片系统也看到了。找了一圈影视系统没找到,而我对搞播放器情有独钟。(失业原因)闲着,绝定写一套基本的nodejs影视系统。

<<< 用nodejs+mongodb开发了套影视站点cms,觉得可以的大佬可以送个star >>>

<<< 一款flutter fijkplayer播放器皮肤(fijkplayer_skin) >>>

思考1:无论是做影视系统还是音乐系统,前提是有足够多的数据。数据怎么办?

玩了那么多年播放器,都知道爪巴呗,搜的话,能搜到很多滋援王,差不多都有开放的api供你爪巴。你可以对接比如苹果cms的接口,例如xxx.com/macapi/?ac=videolist&pg=1 ,

内容规划(后台部分)
按功能来划分,视频数据,留言模块,用户模块,配置模块,其他模块(导航,分类),模板模块,定时认为模块,脚本管理模块。

前台部分

按页面来分,主页面(展示各类信息),详情页(展示单条数据的信息),播放页(展示单条数据的信息+播放),个人中心(修改密码,修改昵称),分类页(全部分类,年代,地区),搜索页(搜索数据),导航页(某个导航下的所有分类数据)

先要确定分类字段,在电影网站的分类页面,都会有分类的单独描述,关键字(这里简称seo信息)。导航下也许存在二级分类(可能需要联表查询子分类),导航可以设置显示隐藏。导航万一要调整输出顺序呢?(所以index位置少不了)
name(用于导航名称),display(显示隐藏),parent_id(存在父级导航,那么存父级的_id,不存在则存false,用于判断真假,是否是一级导航或者二级分类),seo信息用于存储

   {
      "name" : "动作片",
      "parent_id" : ObjectId("5e819a8619f4d790bc5d0c76"),
      "display" : true,
      "seo" : {
          "title" : "动作片",
          "keywords" : "动作片",
          "description" : "动作片"
      },
      "type" : "nav_type",
      "index" : 0  
  }

视频数据(后台)

先要确定数据字段,有哪些字段??字段设置成什么类型合适??
这里我参照资源网的来思考做

标题,导演,主演,更新时间,描述,更新状态,封面,语言,地区,发布时间。这几个字段是必须要有的,
这里需要注意的是,如果搜索中药做演员表索引搜索的话,最好吧演员表这里设置成数组.例: [“靳东”,“蒋欣”];
这样在mongodb中使用$in操作符方便查找演员,我这里因为不做演员索引,索引就直接字符串来存。
如果不是存的数组,存字符串来查找演员,那么免费版芒果,只能用正则,很浪费时间

nodejs + mongodb开发一套影视电影cms系统_第1张图片

分类怎么办?

一个电影在展示的时候是需要找对应的分类,如果字段写死了,那么后期想修改分类的名称,就要找到所有的视频数据,批量替换旧的分类名称,这就不好了。最好的办法就是《联表查询》,mongo提供了联表查询的操作,使用管道aggregate操作符来联表查询

{
    $lookup: {
        from: "other",                   // 关联的表 名称
        localField: "video_type",        // 当前表的字段 需要关联到目标表
        foreignField: "_id",             // 目标表和当前表字段对应的字段
        as: "type"                       // 输出的字段
    }
},
{
    $unwind: "$type"
}

用户模块

用户信息最少也需要有三个字段,username(用户名), password(密码),nickname(昵称)
其他的比如,显示隐藏display, 权限位置高低grade_id,是否默认default,可以做,这里以最简单的为例子
密码可以md5或者md5+hash,或者加点盐??
_id 是mongodb插入时自带的唯一索引的字段,对于分布式来说解决了id重复的问题,我们可以直接用。省去了自己生成id的麻烦事了,自带唯一索引

{
    "_id" : ObjectId("5e7e35cf4345c47a1c8c15f6"),
    "userName" : "abcdxxxx",
    "passWord" : "ba0a086c8a7b0ca4232406b5efff3a95",
    "nickName" : "阿打算",
    "admin" : false,
    "display" : true,
    "default" : false,
    "grade_id" : 0
}

留言模块

可能新同学(这里指的是cxk打篮球的那种前端去搞留言)做留言的时候麻烦过,怎么规划?有啥字段。
这里以楼中楼形式留言为模型。

nodejs + mongodb开发一套影视电影cms系统_第2张图片

数据字段如下:

  • _id系统自带,用于表示本条数据的唯一id,
  • vid用于关联视频表中对应视频的唯一_id,
  • uid用于关联用户表中对应的用户的唯一_id,
  • pid用于查找本条留言的父一级(这里指盖楼那个小崽子那条留言的_id)
  • wid楼里面的留言肯定是对某某人进行的,你回复了哪个鳖孙??就是那个鳖孙的唯一_id
{
    "_id" : ObjectId("5e7f93a60eefb36e54fe8f72"),
    "vid" : ObjectId("5e7e15b04a285358100e3d6f"),
    "uid" : ObjectId("5e7e35cf4345c47a1c8c15f6"),
    "pid" : false,
    "wid" : false,
    "agree" : true,
    "display" : true,
    "date" : 1585419174015.0,
    "sub_date" : 1585419174014.0,
    "text" : "达大厦"
}

和视频数据关联分类数据一样,我们不能每条留言吧用户名和对谁回复写死,万一对方改名了呢??你全表在找到旧的数据疯狂替换吗?所以这里还得用联表查询。

查找留言的时候,规则如下:
先找到一级评价,也就是盖楼那个鳖孙

nodejs + mongodb开发一套影视电影cms系统_第3张图片

nodejs + mongodb开发一套影视电影cms系统_第4张图片

然后循环这两条数据,用这两条数据_id,去找盖楼的下面的二级回复的数据,找到吧他们加到本条数据(盖楼的那条数据)的children字段里面

nodejs + mongodb开发一套影视电影cms系统_第5张图片

至此留言和用户部分结束。

视频源录入(后台)

上一章说了,视频数据录入格式。播放源如何录入,长啥样?
和留言关联视频表,关联用户表一样。视频源的数据也需要单独出来,因为如果不单独出来,我们只是查询一下电影列表,不需要展示播放源列表的时候,没有必要无端带出来那么多的数据

nodejs + mongodb开发一套影视电影cms系统_第6张图片

同样使用mongodb管道联表操作就可以很容易关联出源的数据。

初始化数据(后台)

项目初始化默认就必须有的数据,比如博客总得有个默认的后台系统的root用户吧,默认的顶级分类,一些默认的配置,从哪来??肯定不会天上掉下来。

nodejs pakcage。json的script字段用于定义一些命令。前端的肯定知道,npm run dev,npm run build这些你肯定常用

你完全可以再script定义你的初始化数据命令。

"scripts": {
    "build": "node ./build/initDataBase.js",
    "restore": "mongorestore -d movie ./backup/movie",
    "backup": "mongodump -d movie -o ./backup/movie"
}

build用于连接数据库,删除原有的数据库,创建全新的数据库,创建字段索引,并向数据库中插入默认的数据,比如电影cms系统的默认管理员信息。初始化的数据

const mongodb = require('mongodb'),
MongoClient = mongodb.MongoClient,
config = require('../utils/config.js'),
dbConfig = config.project;

let connectURL = 'mongodb://127.0.0.1:27017';

let client = MongoClient.connect(connectURL, { useNewUrlParser: true, useUnifiedTopology: true });

client.then(async (db) => {

    let DB = db.db(dbConfig.dbName);

    await new Promise(async (resolve, reject) => {

        console.log('正在清除原始数据表');
        await DB.dropCollection('session1').catch(err => {});
        await DB.dropCollection('session2').catch(err => {});
        await DB.dropCollection('config').catch(err => {});
        await DB.dropCollection('logs').catch(err => {});
        await DB.dropCollection('message').catch(err => {});
        await DB.dropCollection('other').catch(err => {});
        await DB.dropCollection('user').catch(err => {});
        await DB.dropCollection('video_info').catch(err => {});
        await DB.dropCollection('video_list').catch(err => {});
        resolve()
    })
    .then(() => {
        console.log('原始数据表清除完成');
    })
    .catch(err => {
        console.log('+++', err);
    })
    
    await new Promise(async (resolve, reject) => {
        console.log('开始重建数据表');
        await DB.createCollection('session1');
        await DB.createCollection('session2');
        await DB.createCollection('config');
        await DB.createCollection('logs');
        await DB.createCollection('message');
        await DB.createCollection('other');
        await DB.createCollection('user');
        await DB.createCollection('video_info');
        await DB.createCollection('video_list');
        resolve()
    })
    .then(() => {
        console.log('数据表重建完成');
    })
    await new Promise(async (resolve, reject) => {
        console.log('开始创建用户数据');
        // 用户字段索引
        let userColl =  DB.collection('user');
        // 索引字段
        await userColl.createIndexes([
            {key:{userName: 1}},
            {key:{passWord: 1}},
            {key:{display: 1}},
            {key:{admin: 1}},
            {key:{grade_id: 1}},
            {key:{default: 1}}
        ]);
        await userColl.insertOne({
            userName: 'root',
            passWord: 'e10adc3949ba59abbe56e057f20f883e',  // 123456 md5
            nickName: '网站所有者',
            admin: true,
            display: true,
            default: true,
            grade_id: 2,                                   // 0用户 1管理员 2root用户
        });
        resolve();
    })
    .then(res=>{
        console.log('用户数据创建成功');
    })
});

部署问题思考 :pm2部署无法关闭任务(后台)

pm2部署无法关闭任务是因为,用户(管理员)开启定时任务的时候,可能被pm2分发到了B线程(假设电脑6核心12线程),你:我电脑就双核哪来的六核心?

nodejs + mongodb开发一套影视电影cms系统_第7张图片

不好意思,我的12线程。

扯远了,扯回来。
当用户(管理员)想关闭的时候pm2可能把任务分发到了G线程上了,但是G线程上并没有执行这个任务,就会报错。

定时任务被多个线程同时执行?

那是因为这个定时任务是项目部署开启服务的时候就设置好了,到了时间,pm2复制多份任务执行(一毛一样),当然就会重复跑任务。

怎么避免合适?

试想一下,如果我们项目用pm2部署,复制了12份node项目。但是我们可以使用别的进程守护工具,例如forever(单份,单线程)守护定时任务。
说人话:就是分两个项目部署,定时任务使用一个单线程守护工具,守护。这样,定时任务到时间以后就是单线程跑了,不会多线程同时调用。随之而来的一个问题,怎么通讯??pm2那边的服务怎么通知forever这边守护的的进程开启关闭任务。(http服务啊)

解决1:假设pm2守护的项目监听的是8888端口,我们forever这边的js开启的http服务可以监听9999。两者之间通讯就发post请求。(因为本机的原因。所以很快,秒收到)这样,通讯的问题解决,就可以随时动态的创建/删除 定时任务了。也不会出现多线程同时执行的问题。

解决2:使用系统底层提供的定时任务接口,比如linux下的cronTab,windows下我不知道,避免pm2重复执行

使用宝塔面板懒人部署nodejs项目

进入宝塔面板软件商店,安装pm2管理器,mongodb,nginx

nodejs + mongodb开发一套影视电影cms系统_第8张图片
nodejs + mongodb开发一套影视电影cms系统_第9张图片

安装完成以后,点击文件,上传网站压缩文件,zip

nodejs + mongodb开发一套影视电影cms系统_第10张图片
nodejs + mongodb开发一套影视电影cms系统_第11张图片

// 进入网站所在目录(宝塔默认网站根目录/www/wwwroot)
cd /www/wwwroot

// 安装依赖
npm install

// 初始化数据
npm run build

进入pm2管理器,选择网站

nodejs + mongodb开发一套影视电影cms系统_第12张图片

这里其实相当于进入项目目录,执行(↑上面的操作相当于执行了这条命令)

pm2 start app.js --name app

到这里一步只是启动了项目,但是没有映射(也就是设置转发请求),因为最前面有nginx接受了所有的请求,选择映射就是让nginx吧某个网站请求转给pm2管理器开启的服务

nodejs + mongodb开发一套影视电影cms系统_第13张图片

映射完成之后就可以再网站一栏找到刚才映射的网站。

nodejs + mongodb开发一套影视电影cms系统_第14张图片

部署完成效果

nodejs + mongodb开发一套影视电影cms系统_第15张图片
nodejs + mongodb开发一套影视电影cms系统_第16张图片

你可能感兴趣的:(node.js,vue.js,es6,javascript,mongodb)