node.js学习入门示例

阅读更多
  这些天有点儿空闲,学习了一下node.js。先把《Node.js开发指南》书中的博客例子调通了,费了挺大的劲儿,因为好多插件出了新的版本,而新版本中有的函数、方法又失效了。
  自已又练习着做了一个例子,个人记账系统。主要是想在手机上用,所以界面做得很简单。
  解决了以下一些问题:express使用、bootstrap排版布局、mongodb模糊查询、mongodb统计(group/mapReduce)、session处理、req.flash方法使用等、路由设置等。花了不少的心思。
  例子中用到的插件的版本:
  [email protected]
  [email protected]
  [email protected]
  [email protected]
  [email protected]

  Bootstrap V3.0.3

  贴几张系统运行的图片,有图有真相嘛。

node.js学习入门示例_第1张图片

node.js学习入门示例_第2张图片

node.js学习入门示例_第3张图片

node.js学习入门示例_第4张图片

node.js学习入门示例_第5张图片



  核心代码贴一下:
一、app.js

/**
 * Module dependencies.
 */

var express = require('express');
var http = require('http');
var path = require('path');
var util=require('util');

var routes = require('./routes');
//var User = require('./modules/user.js');	
var settings=require('./Settings');

var MongoStore = require('connect-mongo')(express);
var flash = require('connect-flash');

var app = express();

//app.set('appTitle','老王个人记账系统');
app.locals.gAppTitle = settings.appName;
//这个没有使用
app.locals.gPageSize = settings.pageSize;
// all environments
app.set('port', process.env.PORT || 8484);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.bodyParser());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(express.cookieParser());
app.use(flash());
app.use(express.session({
	"secret":settings.cookieSecret,
	"store":new MongoStore({
		db:settings.db
	})
}));

app.use(function(req, res, next){
  //跟踪;
  //console.log("req.method="+req.method);
  //console.log("req.url="+req.url);
  //console.log("req.originalUrl="+req.originalUrl);
  var url = req.originalUrl;
  //简单地定义一个登录拦截器
  if ((url == "/month" || url=="/stat" || url=='/list' || url=='/record') && !req.session.user) {
  	  console.log("登录拦截器提示:必须登录,才能执行此项操作。");
  	  req.flash('error', '请先登录。');
      return res.redirect("/login");
  }
  
  res.locals.user = req.session.user;
  
  var error = req.flash('error');
  res.locals.error = error.length?error:null;
  //console.log("转移flash中的error值:"+error);
  
  var success = req.flash('success');
  res.locals.success = success.length?success:null;
  //console.log("转移flash中的success值:"+success);
  
  res.locals.session = req.session;
  next();
});

app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
//console.log(util.inspect(app));

// development only
if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}

//console.log('注册路由.');

routes(app);


http.createServer(app).listen(app.get('port'), function(){
	console.log();
	console.log();
	console.log('/**************************************************/');
	console.log('/*  我的第一个NODE.JS例子。BY 隔壁老王 2014-3-29  */');
	console.log('/*   欢迎访问我的博客:http://wallimn.iteye.com   */');
	console.log('/**************************************************/');
  	console.log('============服务启动成功,监听端口:' + app.get('port')+"============");
});


二、路由处理(routes\index.js)
var crypto = require('crypto');
var User = require('../modules/user.js');	
var Consume = require('../modules/consume.js');
/*
 * GET home page.
 */

module.exports = function(app) {
	app.get('/',function(req, res){
	  res.render('index', { title: '首页' });
	  /*
		res.render('login',{
			title:'用户登录',
		});
	  */
	  //res.redirect('/login');
	});

	
	app.get('/record',function(req,res){
		var user = req.session.user;
		if(!user){
			req.flash('error', '用户未登录,请登录。');
			return res.redirect('/login');
		}

		Consume.get(user.name,{limit:16}, function(err, records) {
			if (err) {
				req.flash('error', err);
				return res.redirect('/error');
			}
			res.render('record', {
				title: user.name,
				consumes: records,
			});
		});
	});
	
	//预处理,如果通过,再进行下一个。
	app.post('/record',checkLogin);

	app.post('/record', function(req, res) {
		var currentUser = req.session.user;
		var record = new Consume();
		record.loadFromReq(currentUser.name, req.body);
		record.save(function(err) {
			if (err) {
				req.flash('error', err);
				return res.redirect('/');
			}
			req.flash('success', '发表成功');
			res.redirect('/record');
		});
	});

	//这个应该去掉。留在这里当个后门吧
	//可以查看其他用户的数据
	app.get('/u/:user', function(req, res) {
		var username = req.params.user;
		if (!username) {
			req.flash('error', '未指定用户');
			return res.redirect('/error');
		}

		Consume.get(username,{limit:0}, function(err, records) {
			if (err) {
				req.flash('error', err);
				return res.redirect('/error');
			}
			console.log(records);
			res.render('list', {
				title: username,
				consumes: records,
			});
		});
	});
	
	app.get('/del/:id', function(req, res) {
		var id = req.params.id;
		if (!id) {
			req.flash('error', '未指定要删除的记录ID');
			return res.redirect('/error');
		}
		
		console.log("准备删除记账记录,_id="+id);
		Consume.del(id, function(err, records) {
			if (err) {
				req.flash('error', err);
				return res.redirect('/error');
			}
			res.redirect('/record');;
		});
	});
	
	app.get('/logout',function(req,res){
		req.session.user = null;
		req.flash('success','登出成功');
		res.redirect('/login');
	});
	
	app.get('/list',function(req,res){
		var user = req.session.user;
		if(!user){
				req.flash('error', "您没有登录,请登录。");
				console.log("没有登录,重定向的登录界面。");
				return res.redirect('/login');
		}
		Consume.get(user.name,{limit:0}, function(err, records) {
			if (err) {
				req.flash('error', err);
				return res.redirect('/error');
			}
			res.render('list', {
				title: user.name,
				consumes: records,
			});
		});
	});
	
	app.post('/search',function(req,res){
		var user = req.session.user;
		if(!user){
				req.flash('error', "您没有登录,请登录。");
				return res.redirect('/login');
		}
		var keyword = req.body.keyword;
		console.log("搜索关键字:"+keyword);
		Consume.get(user.name,{limit:0,keyword:keyword}, function(err, records) {
			if (err) {
				req.flash('error', err);
				return res.redirect('/error');
			}
			res.render('record', {
				title: user.name,
				consumes: records,
			});
		});
	});
	
	app.get('/stat',function(req,res){
		var user = req.session.user;
		Consume.stat(user.name, function(err, results) {
			if (err) {
				req.flash('error', err);
				return res.redirect('/error');
			}
			res.render('stat', {
				title: user.name,
				results: results,
			});
		});
	});
	
	app.get('/month',function(req,res){
		var user = req.session.user;
		Consume.month(user.name, function(err, results) {
			if (err) {
				req.flash('error', err);
				return res.redirect('/error');
			}
			res.render('stat', {
				title: user.name,
				results: results,
			});
		});
	});
	
	app.get('/error',function(req,res){
		res.render('error');
	});

	//处理用户登录。
	app.post('/login',function(req,res){
		var md5=crypto.createHash('md5');
		var password = md5.update(req.body.password).digest('hex');
		
		User.get(req.body.username,function(err,user){
			if(!user){
				req.flash('error','用户不存在');
				return res.redirect('/login');
			}
			
			if(user.password!=password){
				req.flash('error','用户口令错误');
				return res.redirect('/login');
			}
			
			req.session.user = user;
			req.flash('success','登入成功');
			//res.redirect('/');
			res.redirect('/record');
		});
	});

	app.get('/login',function(req,res){
		res.render('login',{
			title:'用户登录',
		});
	});

	app.get('/reg',function(req,res){
		
		res.render('reg',{
			title:'用户注册'
		});
	});

	app.post('/reg',function(req,res){
		if(req.body['password-repeat']!=req.body['password']){
			req.flash('error','两次输入的口令不一致!');
			return res.redirect('/reg');
		}
		
		var md5=crypto.createHash('md5');
		var password = md5.update(req.body.password).digest('hex');
		
		var newUser = new User({
			name:req.body.username,
			password:password,
		});
		
		User.get(newUser.name,function(err,user){
			if(user)
				err='同名用户已经存在,请更换名字.';
			if(err){
				req.flash('error',err);
				return res.redirect('/reg');
			}
			
			newUser.save(function(err){
				if(err){
					req.error=err;
					return res.redirect('/reg');
				}	
				req.session.user = newUser;
				req.flash('success','注册成功!');
				res.redirect('/record');
			});
		});
	});

	//测试函数
	app.get('/hello',function(req,res){
		res.send('The time is '+new Date().toString());
	});

	//测试函数
	app.get('/sayhello',function(req,res){
		res.send('hello '+req.params.username);
	});
	
};

//检查是否登入.
function checkLogin(req, res, next) {
	if (!req.session.user) {
		req.flash('error', '尚未登录,无法操作。');
		return res.redirect('/error');
	}
	next();
}

function checkNotLogin(req, res, next) {
	if (req.session.user) {
		req.flash('error', '已登入');
		return res.redirect('/');
	}
	next();
}


三、消费数据处理(modules\consume.js)
var mongodb = require('./db');
var BSON = require('mongodb').BSONPure;
var util=require('util');


function toObjectId(id){
	console.log("转换值:"+id);
	if( id=="" || id=="null" || id=="undefined" || id==undefined || id==null)return null;
	return BSON.ObjectID.createFromHexString(id);
}

function Consume(username, consumeDate,consumeSubject,consumeAmount,consumeRemark, time)
{
	//加载时要单独赋值
	this._id=null;
	
    this.userName = username;
    this.consumeDate = consumeDate;
    this.consumeSubject = consumeSubject;
    this.consumeAmount = consumeAmount;
    this.consumeRemark = consumeRemark;

    if (time)
    {
        this.time = time;
    }
    else
    {
        this.time = new Date();
    }
    
};
module.exports = Consume;

Consume.prototype.loadFromReq = function loadFromReq(username,reqBody,time){
	//自动进行了ID类型的转换。
	this._id = toObjectId(reqBody._id);
    this.userName = username;
    this.consumeDate = reqBody.consumeDate;
    this.consumeSubject = reqBody.consumeSubject;
    this.consumeAmount = reqBody.consumeAmount;
    this.consumeRemark = reqBody.consumeRemark;

    if (time)
    {
        this.time = time;
    }
    else
    {
        this.time = new Date();
    }
}

Consume.prototype.save = function save(callback)
{
    // 存入 Mongodb 的文档
    var record = 
    {
    	_id:this._id,
        userName: this.userName, 
        consumeDate: this.consumeDate, 
        consumeSubject: this.consumeSubject, 
        consumeAmount: this.consumeAmount, 
        consumeRemark: this.consumeRemark, 
        time: this.time, 
    };
    console.log('保存,记录日期:'+record.consumeDate);
    mongodb.open(function(err, db){
        if (err)
        {
            return callback(err); 
        }
        // 读取 posts 集合
        db.collection('consume', function(err, collection){
            if (err)
            {
                mongodb.close(); return callback(err); 
            }

            // 插入
            /*
            collection.insert(record, {safe: true} , function(err, post){
                mongodb.close(); callback(err, post); 
            }); 
            */
           
           console.log("插入或更新,判断依据_id="+record._id);
		   if(record._id==null){
		   	   delete record._id;
		   	   console.log("删除_id,record._id="+record._id);
		   }
           collection.update({_id:(record._id?record._id:'no-record')}, record, {upsert:true,multi:false} , function(err, post){
                mongodb.close(); callback(err, post); 
            }); 
        }); 
    });
};

//删除方法
Consume.del = function del(id,callback){
	mongodb.open(function(err,db){
        if (err){
            return callback(err); 
        }
        
		var query = {_id:BSON.ObjectID.createFromHexString(id)};
		
        db.collection('consume', function(err, collection){
            if (err){
                mongodb.close(); 
                return callback(err); 
            }

			collection.remove(query,{safe:true},function(err,result){
				mongodb.close();
		        if (err){
		            return callback(err); 
		        }
		        console.log("删除成功。");
				callback(null); 
		    }) ;
		});    
   	});
};



Consume.get = function get(username,options, callback)
{
    mongodb.open(function(err, db){
        if (err){
            return callback(err); 
        }
        // 读取 posts 集合
        db.collection('consume', function(err, collection){
            if (err){
                mongodb.close();
                return callback(err); 
            }

            var query = {};
            if(options.keyword){
            	//var regx = new RegExp("/"+options.keyword+"/");
            	//注意,不用/
            	var regx = new RegExp(options.keyword);
            	//限制用户名,科目或者金额与输入关键相等
				query={"$and":[{userName:username},
							   {"$or":[{consumeSubject:regx},
									   {consumeDate:regx},
							   		   {consumeAmount:options.keyword}
									  ]
							   }]
					   };
            }
            else{
                query.userName = username; 
            }
            console.log("搜索条件:");
            console.log(query);
            if(!options.limit){
            	options.limit=0;
            }
            collection.find(query).sort({consumeDate:-1 }).limit(options.limit).toArray(function(err, docs){
                mongodb.close(); 
                if (err){
                    callback(err, null); 
                }

                var consumes = []; 
                docs.forEach(function(doc, index){
                    var record = new Consume(doc.userName, doc.consumeDate,doc.consumeSubject,doc.consumeAmount,doc.consumeRemark, doc.time);
                 	record._id = doc._id;
                    consumes.push(record); 
                }); 
                callback(null, consumes); 
            }); 
        }); 
    });
};



Consume.stat = function stat(username, callback)
{
    mongodb.open(function(err, db){
        if (err){
            return callback(err); 
        }
        // 读取 posts 集合
        db.collection('consume', function(err, collection){
            if (err){
                mongodb.close();
                return callback(err); 
            }

			var reduce = function(obj,prev){
					prev.amount += isNaN(obj.consumeAmount)?0:Number(obj.consumeAmount);
					prev.count++;
			};
		
            collection.group(
            	[ 'consumeSubject' ],
        		{userName:username},
        		{count:0,amount:0},
            	reduce,
            	function(err, result){
                	mongodb.close(); 
	                if (err){
	                    callback(err, null); 
	                }
	                else{
	                	console.log(result[0]);
	                	var amount = 0,count=0;
	                	result.forEach(function (item,index){
	                		amount += item.amount;
	                		count += item.count;
	                	});
	                	result.push({consumeSubject:'【合计】',count:count,amount:amount});
	                	callback(null, result); 
	                }
	        });
        }); 
    });
};

Consume.month = function month(username, callback)
{
    mongodb.open(function(err, db){
        if (err){
            return callback(err); 
        }
        // 读取 posts 集合
        db.collection('consume', function(err, collection){
            if (err){
                mongodb.close();
                return callback(err); 
            }
            
			var map = function(){
				emit(this.consumeDate.substr(0,7),{amount:this.consumeAmount,count:1});
			};
			
			var reduce = function(key,vals){
				var val = 0,count=0;
				for(var i=0; i 
 

   全部源码见附件。

  另,2014-04-02
  系统开发好了之后,找了个服务器部署了一下,老婆用得不错,原来不太喜欢记账,现在记账很积极。我就又把系统完善了一下,增加了翻页、权限控制功能,将几个按钮修改成图标,布局更紧凑了一些,使用全局变量保存系统名称,方便修改。
  • node.js学习入门示例_第6张图片
  • 大小: 61.2 KB
  • node.js学习入门示例_第7张图片
  • 大小: 77.5 KB
  • node.js学习入门示例_第8张图片
  • 大小: 53.5 KB
  • node.js学习入门示例_第9张图片
  • 大小: 42 KB
  • node.js学习入门示例_第10张图片
  • 大小: 47 KB
  • account.rar (297 KB)
  • 描述: 个人记账系统源码
  • 下载次数: 1012
  • microblog.rar (250.8 KB)
  • 描述: 调试通过的书中例子
  • 下载次数: 529
  • account(查询后修改保持查询条件V0.0.6).rar (308.1 KB)
  • 描述: 查询后保持查询条件,较新的版本。
  • 下载次数: 418
  • 查看图片附件

你可能感兴趣的:(mongodb,node.js,bootstrap)