Authored by 邱骏

Merge branch 'feature/activity-image-upload' of git.yoho.cn:fe/yoho-activity-pla…

…tform into feature/activity-image-upload
@@ -140,6 +140,67 @@ const userController = { @@ -140,6 +140,67 @@ const userController = {
140 res.end(exportFile, 'binary'); 140 res.end(exportFile, 'binary');
141 }) 141 })
142 .catch(next); 142 .catch(next);
  143 + },
  144 + userLoginLog(req, res) {
  145 + res.render('user/login-log', {
  146 + bodyClass: 'nav-md',
  147 + module: 'admin',
  148 + page: 'user-login-log'
  149 + });
  150 + },
  151 + exportUserLoginLog(req, res, next) {
  152 + const url = req.query.url;
  153 + const startTime = req.query.startTime;
  154 + const endTime = req.query.endTime;
  155 +
  156 + let conf = {
  157 + name: 'mysheet',
  158 + cols: [
  159 + {
  160 + caption: '用户id',
  161 + type: 'string'
  162 + },
  163 + {
  164 + caption: '登录时间',
  165 + type: 'string'
  166 + },
  167 + {
  168 + caption: '平台',
  169 + type: 'string'
  170 + },
  171 + {
  172 + caption: '地址',
  173 + type: 'string'
  174 + },
  175 + {
  176 + caption: 'ip',
  177 + type: 'string'
  178 + }
  179 + ],
  180 + rows: []
  181 + };
  182 +
  183 + req.ctx(UserModel).exportUserLoginLog({url, startTime, endTime})
  184 + .then(result => {
  185 + let temp = [];
  186 +
  187 + _.each(result, item => {
  188 + temp = [];
  189 + temp.push(item.user_id);
  190 + temp.push(moment(item.login_time).format('YYYY-MM-DD HH:mm:ss'));
  191 + temp.push(item.platform);
  192 + temp.push(item.referer);
  193 + temp.push(item.ip);
  194 + conf.rows.push(temp);
  195 + });
  196 +
  197 + let exportFile = excelExport.execute(conf);
  198 +
  199 + res.setHeader('Content-Type', 'application/vnd.openxmlformats');
  200 + res.setHeader('Content-Disposition', 'attachment; filename=user-login-log.xlsx');
  201 + res.end(exportFile, 'binary');
  202 + })
  203 + .catch(next);
143 } 204 }
144 }; 205 };
145 206
@@ -8,6 +8,7 @@ const mysqlCli = global.yoho.utils.mysqlCli; @@ -8,6 +8,7 @@ const mysqlCli = global.yoho.utils.mysqlCli;
8 8
9 const _ = require('lodash'); 9 const _ = require('lodash');
10 const TABLE_USER = 'user'; 10 const TABLE_USER = 'user';
  11 +const TABLE_USER_LOGIN_LOG = 'act_login_user_log';
11 12
12 class UserModel extends global.yoho.BaseModel { 13 class UserModel extends global.yoho.BaseModel {
13 constructor(ctx) { 14 constructor(ctx) {
@@ -64,6 +65,26 @@ class UserModel extends global.yoho.BaseModel { @@ -64,6 +65,26 @@ class UserModel extends global.yoho.BaseModel {
64 ); 65 );
65 } 66 }
66 67
  68 + /**
  69 + * 导出用户登录日志
  70 + * @returns {*}
  71 + */
  72 + exportUserLoginLog({url, startTime, endTime}) {
  73 + let sql = `select user_id,login_time,platform,ip,referer from ${TABLE_USER_LOGIN_LOG} where 1=1`;
  74 +
  75 + if (url) {
  76 + sql += ' and referer like :url';
  77 + }
  78 + if (startTime) {
  79 + sql += ' and login_time >= :startTime';
  80 + }
  81 + if (endTime) {
  82 + sql += ' and login_time <= :endTime';
  83 + }
  84 +
  85 + return mysqlCli.query(sql, {url: `%${url}%`, startTime, endTime});
  86 + }
  87 +
67 } 88 }
68 89
69 module.exports = UserModel; 90 module.exports = UserModel;
@@ -21,6 +21,10 @@ router.get('/activity/createArticle', activity.createArticlePage); @@ -21,6 +21,10 @@ router.get('/activity/createArticle', activity.createArticlePage);
21 // 用户管理[page] 21 // 用户管理[page]
22 router.get('/user/list', user.userListPage); 22 router.get('/user/list', user.userListPage);
23 23
  24 +// 用户登录日志[page]
  25 +router.get('/user/login-log', user.userLoginLog);
  26 +router.get('/user/exportUserLoginLog', user.exportUserLoginLog);
  27 +
24 28
25 // 管理员[ajax] 29 // 管理员[ajax]
26 router.post('/api/login', admin.login); 30 router.post('/api/login', admin.login);
  1 +<!-- page content -->
  2 +<div class="right_col" role="main">
  3 + <div class="">
  4 + <div class="row">
  5 + <div class="col-md-12 col-sm-12 col-xs-12">
  6 + <div class="x_panel">
  7 + <div class="x_title">
  8 + <form class="form-horizontal">
  9 + <div class="form-group">
  10 + <input type="text" id="url" name="url" placeholder="输入查询的url" class="form-control" style="width:350px;">
  11 + </div>
  12 + <div class="form-group">
  13 + <input type="text" id="time" name="time"
  14 + class="form-control"
  15 + placeholder="开始时间 - 结束时间" style="width:350px;"/>
  16 + </div>
  17 + <div class="form-group">
  18 + <button type="button" class="btn btn-primary btn-export">导出</button>
  19 + </div>
  20 + <div class="clearfix"></div>
  21 + </form>
  22 + </div>
  23 + </div>
  24 + </div>
  25 + </div>
  26 + </div>
  27 +</div>
  28 +<!-- /page content -->
@@ -13,18 +13,6 @@ _.mergeWith(baseConfig, { @@ -13,18 +13,6 @@ _.mergeWith(baseConfig, {
13 output: { 13 output: {
14 publicPath: devInfo.publicPath 14 publicPath: devInfo.publicPath
15 }, 15 },
16 - module: {  
17 - rules: [{  
18 - enforce: 'pre',  
19 - test: /(\.js|\.vue)$/,  
20 - exclude: /node_modules/,  
21 - loader: 'eslint-loader',  
22 - options: {  
23 - cache: true,  
24 - formatter: require('eslint/lib/formatters/codeframe')  
25 - }  
26 - }]  
27 - },  
28 devServer: { 16 devServer: {
29 host: devInfo.host, 17 host: devInfo.host,
30 port: devInfo.port, 18 port: devInfo.port,
@@ -56,6 +56,7 @@ @@ -56,6 +56,7 @@
56 <li><a><i class="fa fa-users"></i> 用户管理 <span class="fa fa-chevron-down"></span></a> 56 <li><a><i class="fa fa-users"></i> 用户管理 <span class="fa fa-chevron-down"></span></a>
57 <ul class="nav child_menu"> 57 <ul class="nav child_menu">
58 <li><a href="/admin/user/list">用户列表</a></li> 58 <li><a href="/admin/user/list">用户列表</a></li>
  59 + <li><a href="/admin/user/login-log">用户登录日志</a></li>
59 </ul> 60 </ul>
60 </li> 61 </li>
61 </ul> 62 </ul>
@@ -50,6 +50,7 @@ @@ -50,6 +50,7 @@
50 "fast-safe-stringify": "^1.2.0", 50 "fast-safe-stringify": "^1.2.0",
51 "feed": "^1.1.0", 51 "feed": "^1.1.0",
52 "geetest": "^4.1.2", 52 "geetest": "^4.1.2",
  53 + "jsonp": "^0.2.1",
53 "lodash": "^4.17.4", 54 "lodash": "^4.17.4",
54 "mOxie": "^1.5.5", 55 "mOxie": "^1.5.5",
55 "memory-cache": "^0.1.6", 56 "memory-cache": "^0.1.6",
1 import qs from 'yoho-qs'; 1 import qs from 'yoho-qs';
  2 +import cookie from 'yoho-cookie';
  3 +import jsonp from 'jsonp';
2 import 'whatwg-fetch'; 4 import 'whatwg-fetch';
3 5
4 const uid = qs.yh_uid; 6 const uid = qs.yh_uid;
@@ -10,22 +12,38 @@ if (process.env.NODE_ENV === 'production') { @@ -10,22 +12,38 @@ if (process.env.NODE_ENV === 'production') {
10 baseUrl = 'http://localhost:6006'; 12 baseUrl = 'http://localhost:6006';
11 } 13 }
12 14
  15 +const loginLog = userId => {
  16 + if (!cookie.get('_loginLog')) {
  17 + fetch(`${baseUrl}/user/loginLog`, {
  18 + method: 'POST',
  19 + headers: {
  20 + 'Content-Type': 'application/json'
  21 + },
  22 + body: JSON.stringify({
  23 + uid: userId
  24 + })
  25 + }).then(res => {
  26 + if (res.ok) {
  27 + cookie.set('_loginLog', true);
  28 + }
  29 + });
  30 + }
  31 +};
  32 +
13 if (!uid) { 33 if (!uid) {
14 - fetch(`${baseUrl}/user/getLoginUrl?url=${window.location.href}`).then(res => {  
15 - if (res.ok) {  
16 - res.json().then(data => {  
17 - window.location.href = data.loginUrl; 34 + jsonp('http://m.yohobuy.com/passport/login/user', {}, (errMsg, userRes) => {
  35 + if (userRes.code !== 200) {
  36 + fetch(`${baseUrl}/user/getLoginUrl?url=${window.location.href}`).then(res => {
  37 + if (res.ok) {
  38 + res.json().then(data => {
  39 + window.location.href = data.loginUrl;
  40 + });
  41 + }
18 }); 42 });
  43 + } else {
  44 + loginLog(userRes.data);
19 } 45 }
20 }); 46 });
21 } else { 47 } else {
22 - fetch(`${baseUrl}/user/loginLog`, {  
23 - method: 'POST',  
24 - headers: {  
25 - 'Content-Type': 'application/json'  
26 - },  
27 - body: JSON.stringify({  
28 - uid  
29 - })  
30 - }); 48 + loginLog(uid);
31 } 49 }
@@ -185,7 +185,7 @@ function bind_qiniu_upload() { @@ -185,7 +185,7 @@ function bind_qiniu_upload() {
185 185
186 // why? reference: https://github.com/qiniu/js-sdk/issues/266 186 // why? reference: https://github.com/qiniu/js-sdk/issues/266
187 Promise.all([ 187 Promise.all([
188 - require('moxie/bin/js/moxie'), 188 + require('mOxie/bin/js/moxie'),
189 require('plupload/js/plupload.dev'), 189 require('plupload/js/plupload.dev'),
190 require('qiniu-js') 190 require('qiniu-js')
191 ]).then(([moxie, plupload]) => { 191 ]).then(([moxie, plupload]) => {
  1 +require('admin/user-login-log.page.css');
  2 +require('bootstrap-daterangepicker');
  3 +require('bootpag/lib/jquery.bootpag.min');
  4 +const moment = require('moment');
  5 +
  6 +function bind_date_picker() {
  7 + if (typeof ($.fn.daterangepicker) === 'undefined') {
  8 + return;
  9 + }
  10 +
  11 + $('#time').daterangepicker({
  12 + timePicker: true,
  13 + timePickerIncrement: 30,
  14 + startDate: moment().add(-30, 'day'),
  15 + endDate: moment(),
  16 + locale: {
  17 + format: 'YYYY-MM-DD HH:mm:ss'
  18 + }
  19 + });
  20 +}
  21 +function bind_button_event() {
  22 + $('.btn-export').click(() => {
  23 + const url = $('#url').val();
  24 + const time = $('#time').val();
  25 + const timeArray = time.split(' - ');
  26 + let startTime, endTime;
  27 +
  28 + if (timeArray.length === 2) {
  29 + startTime = moment(timeArray[0]).valueOf();
  30 + endTime = moment(timeArray[1]).valueOf();
  31 + }
  32 +
  33 + window.open(`/admin/user/exportUserLoginLog?startTime=${startTime}&endTime=${endTime}&url=${url}`, '_blank');
  34 + });
  35 +}
  36 +
  37 +(() => {
  38 + bind_date_picker();
  39 + bind_button_event();
  40 +})();
  1 +@import '~bootstrap-daterangepicker';
@@ -2055,7 +2055,7 @@ debug@0.7.4, debug@~0.7.4: @@ -2055,7 +2055,7 @@ debug@0.7.4, debug@~0.7.4:
2055 version "0.7.4" 2055 version "0.7.4"
2056 resolved "http://npm.yoho.cn/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" 2056 resolved "http://npm.yoho.cn/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39"
2057 2057
2058 -debug@2, debug@2.6.8, debug@^2.1.1, debug@^2.2.0, debug@^2.6.0, debug@^2.6.8: 2058 +debug@2, debug@2.6.8, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.6.0, debug@^2.6.8:
2059 version "2.6.8" 2059 version "2.6.8"
2060 resolved "http://npm.yoho.cn/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" 2060 resolved "http://npm.yoho.cn/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
2061 dependencies: 2061 dependencies:
@@ -4369,6 +4369,12 @@ jsonify@~0.0.0: @@ -4369,6 +4369,12 @@ jsonify@~0.0.0:
4369 version "0.0.0" 4369 version "0.0.0"
4370 resolved "http://npm.yoho.cn/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" 4370 resolved "http://npm.yoho.cn/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
4371 4371
  4372 +jsonp@^0.2.1:
  4373 + version "0.2.1"
  4374 + resolved "http://npm.yoho.cn/jsonp/-/jsonp-0.2.1.tgz#a65b4fa0f10bda719a05441ea7b94c55f3e15bae"
  4375 + dependencies:
  4376 + debug "^2.1.3"
  4377 +
4372 jsonparse@0.0.5: 4378 jsonparse@0.0.5:
4373 version "0.0.5" 4379 version "0.0.5"
4374 resolved "http://npm.yoho.cn/jsonparse/-/jsonparse-0.0.5.tgz#330542ad3f0a654665b778f3eb2d9a9fa507ac64" 4380 resolved "http://npm.yoho.cn/jsonparse/-/jsonparse-0.0.5.tgz#330542ad3f0a654665b778f3eb2d9a9fa507ac64"