目录
项目要求
登录功能的实现
- 用户名不存在时 ,提示用户名不存在,请注册;
- 当密码错误时, 提示密码错误;
mysql存放用户名及密码
CREATE TABLE `crawl`.`user`(
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`username` VARCHAR(45) NOT NULL,
`password` VARCHAR(45) NOT NULL,
`registertime` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY(`id`),
UNIQUE KEY `username_UNIQUE`(`username`))
ENGINE=InnoDB DEFAULT CHARSET=utf8;
index.html实现登录页面
<form id="login-form" method="post" role="form" style="display: block;">
<input ng-model="username" tabindex="1" class="form-control" placeholder="Username" value="">
<input type="password" ng-model="password" tabindex="2" class="form-control" placeholder="Password">
<button id="login-submit" tabindex="4" class="form-control btn btn-login" ng-click="check_pwd()">LOG IN</button>
</form>
angular代码将输入数据暴露给控制器
通过 $scope 对象的 username 和 password 属性获取绑定的用户名和密码,生成json对象传给路由。 如果返回结果是“ok”,则将当前页面跳转至主页面news.html ;否则 msg 显示警告。
$scope.check_pwd = function () {
var data = JSON.stringify({
username: $scope.username,
password: $scope.password
});
$http.post("/users/login", data)
.then(
function (res) {
if(res.data.msg=='ok') {
window.location.href='/news.html';
}else{
$scope.msg=res.data.msg;
}
},
function (err) {
$scope.msg = err.data;
});
};
路由代码检查账户正确性
在上面的angularjs代码中,数据传入后,需要传递给路由,从而判断账户正确性。
在路由代码(user.js)中我们需要做两个判断:
1、用户名是否为空
2、获取密码后检查是否与用户匹配
var userDAO = require('../dao/userDAO');
router.post('/login', function(req, res) {
var username = req.body.username;
var password = req.body.password;
userDAO.getByUsername(username, function (user) {
if(user.length==0){
res.json({msg:'用户不存在!请检查后输入'});
}else {
if(password===user[0].password){
req.session['username'] = username;
res.cookie('username', username);
res.json({msg: 'ok'});
}else{
res.json({msg:'用户名或密码错误!请检查后输入'});
}
}
});
});
注册功能的实现
index.html中预设表单
默认打开时不显示注册界面,所以display设置为none。当点击register时才显示
<form id="register-form" method="post" role="form" style="display: none;">
<input ng-model="add_username" tabindex="1" class="form-control" placeholder="Username" value=""/>
<input type="password" ng-model="add_password" tabindex="2" class="form-control" placeholder="Password">
<input type="password" ng-model="confirm_password" tabindex="2" class="form-control" placeholder="Confirm Password">
<button tabindex="4" class="form-control btn btn-register" ng-click="doAdd()">Register Now</button>
</form>
angular代码
$http.post("/users/register", data)
.then(function (res) {
if(res.data.msg=='成功注册!请登录') {
$scope.msg=res.data.msg;
$timeout(function () {
window.location.href='index.html';
},2000);
} else {
$scope.msg = res.data.msg;
}
}, function (err) {
$scope.msg = err.data;
});
}
路由代码
检查账号是否重复,如果符合注册条件则显示:成功注册!请登录
router.post('/register', function (req, res) {
var add_user = req.body;
userDAO.getByUsername(add_user.username, function (user) {
if (user.length != 0) {
res.json({msg: '用户已存在!'});
}else {
userDAO.add(add_user, function (success) {
res.json({msg: '成功注册!请登录'});
})
}
});
});
操作日志的存储
创建操作日志表单
CREATE TABLE `crawl`.`user_action`(
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`username` VARCHAR(45) NOT NULL,
`request_time` VARCHAR(45) NOT NULL,
`request_method` VARCHAR(20) NOT NULL,
`request_url` VARCHAR(300) NOT NULL,
`status` int(4),
`remote_addr` VARCHAR(1000) NOT NULL,
PRIMARY KEY(`id`))
ENGINE=InnoDB DEFAULT CHARSET=utf8;
app.js将操作计入日志中
app.use(session({
secret: 'sessiontest',//与cookieParser中的一致
resave: true,
saveUninitialized: false, // 是否保存未初始化的会话
cookie : {
maxAge : 1000 * 60 * 60, // 设置 session 的有效时间,单位毫秒
},
}));
let method = '';
app.use(logger(function (tokens, req, res) {
console.log('打印的日志信息:');
var request_time = new Date();
var request_method = tokens.method(req, res);
var request_url = tokens.url(req, res);
var status = tokens.status(req, res);
var remote_addr = tokens['remote-addr'](req, res);
if(req.session){
var username = req.session['username']||'notlogin';
}else {
var username = 'notlogin';
}
// 直接将用户操作记入mysql中
if(username!='notlogin'){
logDAO.userlog([username,request_time,request_method,request_url,status,remote_addr], function (success) {
console.log('成功保存!');
})
}
布尔表达式查询
单独设计search.html模块
search.html作为news.html的一部分展示在主页面中,从而控制查询结果的显示。预览效果为,第一次点进主界面时,显示导航栏,搜索框和云词;当输入数据查询后,显示搜索结果。
为实现此功能,我们将查询框和查询结果放在不同的div中,只有点击查询按钮,出发了search()函数后,才会展示搜索结果
<form class="form-horizontal" role="form">
<div class="row" style="margin-bottom: 10px;">
<label class="col-lg-2 control-label" style="color: aliceblue;">标题关键字</label>
<div class="col-lg-3">
<input type="text" class="form-control" placeholder="标题关键字" ng-model="$parent.title1">
</div>
<div class="col-lg-1">
<select class="form-control" autocomplete="off" ng-model="$parent.selectTitle">
<option selected="selected" >AND</option>
<option >OR</option>
</select>
</div>
<div class="col-lg-3">
<input type="text" class="form-control" placeholder="标题关键字" ng-model="$parent.title2">
</div>
</div>
<div class="row" style="margin-bottom: 10px;">
<label class="col-lg-2 control-label" style="color: aliceblue;">内容关键字</label>
<div class="col-lg-3">
<input type="text" class="form-control" placeholder="内容关键字" ng-model="$parent.content1">
</div>
<div class="col-lg-1">
<select class="form-control" autocomplete="off" ng-model="$parent.selectContent">
<option selected="selected" >AND</option>
<option >OR</option>
</select>
</div>
<div class="col-lg-3">
<input type="text" class="form-control" placeholder="内容关键字" ng-model="$parent.content2">
</div>
</div>
<div class="form-group">
<div class="col-md-offset-9">
<button type="submit" class="btn btn-default" ng-click="search()">查询</button>
</div>
</div>
</form>
搜索结果的关键代码如下,我们将该板块的显示标签定义为isisshowresult
<div class='formshow' ng-show="isisshowresult">
<table class="table table-striped">
<thead>
<tr>
<td>序号</td>
<td>标题</td>
<td>作者</td>
<!-- <td>内容</td>-->
<td>关键词</td>
<td>链接</td>
<td>发布时间</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="(key, item) in items">
<td>{{index+key}}</td>
<td>{{item.title}}</td>
<td>{{item.author}}</td>
<!-- <td>{{item.content}}</td>-->
<td>{{item.keywords}}</td>
<td>{{item.url}}</td>
<td>{{item.publish_date}}</td>
</tr>
</tbody>
</table>
调用search函数
$scope.search = function () {
$scope.isShows =false;
var title1 = $scope.title1;
var title2 = $scope.title2;
var selectTitle = $scope.selectTitle;
var content1 = $scope.content1;
var content2 = $scope.content2;
var selectContent = $scope.selectContent;
var sorttime = $scope.sorttime;
// 检查用户传的参数是否有问题
//用户有可能这样输入:___ and/or 新冠(直接把查询词输在了第二个位置)
if(typeof title1=="undefined" && typeof title2!="undefined" && title2.length>0){
title1 = title2;
}
if(typeof content1=="undefined" && typeof content2!="undefined" && content2.length>0){
content1 = content2;
}
// 用户可能一个查询词都不输入,默认就是查找全部数据
var myurl = `/news/search?t1=${title1}&ts=${selectTitle}&t2=${title2}&c1=${content1}&cs=${selectContent}&c2=${content2}&stime=${sorttime}`;
连接池返回查询结果
由路由段将查询关键字发送给连接池,连接池将关键字拼接后返回查询结果,由时间顺序分页表示
search :function(searchparam, callback) {
// 组合查询条件
var sql = 'select * from fetches ';
if(searchparam["t2"]!="undefined"){
sql +=(`where title like '%${searchparam["t1"]}%' ${searchparam['ts']} title like '%${searchparam["t2"]}%'`);
}else if(searchparam["t1"]!="undefined"){
sql +=(`where title like '%${searchparam["t1"]}%' `);
};
if(searchparam["t1"]=="undefined"&&searchparam["t2"]=="undefined"&&searchparam["c1"]!="undefined"){
sql+='where ';
}else if(searchparam["t1"]!="undefined"&&searchparam["c1"]!="undefined"){
sql+='and ';
}
if(searchparam["c2"]!="undefined"){
sql +=(`content like '%${searchparam["c1"]}%' ${searchparam['cs']} content like '%${searchparam["c2"]}%' `);
}else if(searchparam["c1"]!="undefined"){
sql +=(`content like '%${searchparam["c1"]}%' `);
}
if(searchparam['stime']!="undefined"){
if(searchparam['stime']=="1"){
sql+='ORDER BY publish_date ASC ';
}else {
sql+='ORDER BY publish_date DESC ';
}
}
sql+=';';
console.log(sql);
pool.getConnection(function(err, conn) {
if (err) {
callback(err, null, null);
} else {
conn.query(sql, function(qerr, vals, fields) {
conn.release(); //释放连接
callback(qerr, vals, fields); //事件驱动回调
});
}
});
}
echarts实现图表展示
折线图类
首先需要引用<script src="echarts.min.js"></script>
在echarts官网可以下载
以折线图为例,在news.js中,以line函数为trigger,调用图表
router.get('/line', function(request, response) {
var keyword = request.query.keyword;
var fetchSql = "select content,publish_date from fetches where content like'%" + keyword + "%' order by publish_date;";
newsDAO.query_noparam(fetchSql, function (err, result, fields) {
response.writeHead(200, {
"Content-Type": "application/json",
"Cache-Control": "no-cache, no-store, must-revalidate",
"Pragma": "no-cache",
"Expires": 0
});
response.write(JSON.stringify({message:'data',result:myfreqchangeModule.freqchange(result, keyword)}));
response.end();
});
});
效果如下
词云类
首先定义关键词分割的js代码,并且调用node-analyzer模块
npm install node-analyzer -save
var Segmenter = require('node-analyzer');
//正则表达式去掉一些无用的字符,与高频但无意义的词。
const regex = /[\t\s\r\n\d\w]|[\+\-\(\),\.。,!?《》@、【】"'::%-\/“”]/g;
var wordcut = function(vals) {
var segmenter = new Segmenter();
var word_freq = {};
vals.forEach(function (content){
if(content['content']!=null){
var newcontent = content["content"].replace(regex,'');
if(newcontent.length !== 0){
// console.log();
var words = segmenter.analyze(newcontent).split(' ');
// var words = nodejieba.cut(newcontent);
words.forEach(function (word){
word = word.toString();
word_freq[word] = (word_freq[word] +1 ) || 1;
});
};
}
});
return word_freq;
};
exports.wordcut = wordcut;
词云的触发函数为wordcloud
router.get('/wordcloud', function(request, response) {
//sql字符串和参数
//sql字符串和参数
var fetchSql = "select content from fetches;";
newsDAO.query_noparam(fetchSql, function (err, result, fields) {
response.writeHead(200, {
"Content-Type": "application/json",
"Cache-Control": "no-cache, no-store, must-revalidate",
"Pragma": "no-cache",
"Expires": 0
});
response.write(JSON.stringify({message:'data',result:mywordcutModule.wordcut(result)}));//返回处理过的数据
response.end();
});
});
之后我们让词云在主页上显示,效果如下
效果展示
web编程期末作业
通过这学期的课程,学到了很多知识,同时也看到了自己很多不足。web编程课为我打开了一扇窗,让我看到更广阔的未知的大海,让我认识到了即便是处理从未涉及过的难题,只要一步一步踏实地走下去,也是一定可以有回报的。