Authored by 郭成尧

merge_develop

  1 +root = true
  2 +
  3 +[*]
  4 +indent_style = space
  5 +indent_size = 4
  6 +end_of_line = lf
  7 +charset = utf-8
  8 +trim_trailing_whitespace = true
  9 +insert_final_newline = true
  10 +
  11 +[*.md]
  12 +trim_trailing_whitespace = false
  1 +bundle/**/*.js
  2 +dist/**/*.js
@@ -44,7 +44,7 @@ jspm_packages @@ -44,7 +44,7 @@ jspm_packages
44 ### WebStorm ### 44 ### WebStorm ###
45 # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 45 # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
46 # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 46 # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
47 - 47 +.idea/
48 # User-specific stuff: 48 # User-specific stuff:
49 .idea/workspace.xml 49 .idea/workspace.xml
50 .idea/tasks.xml 50 .idea/tasks.xml
@@ -138,3 +138,4 @@ dist @@ -138,3 +138,4 @@ dist
138 public/css/* 138 public/css/*
139 public/bundle/* 139 public/bundle/*
140 .eslintcache 140 .eslintcache
  141 +*.log.*
  1 +css/**/*.css
  2 +dist/**/*.css
@@ -3,21 +3,26 @@ @@ -3,21 +3,26 @@
3 * @author: xuqi<qi.xu@yoho.cn> 3 * @author: xuqi<qi.xu@yoho.cn>
4 * @date: 2016/4/25 4 * @date: 2016/4/25
5 */ 5 */
  6 +'use strict';
6 7
7 -var express = require('express'), 8 +let express = require('express'),
8 path = require('path'), 9 path = require('path'),
9 bodyParser = require('body-parser'), 10 bodyParser = require('body-parser'),
10 - favicon = require('serve-favicon'),  
11 - cookieParser = require('cookie-parser'); 11 + cookieParser = require('cookie-parser'),
  12 + favicon = require('serve-favicon');
12 13
13 -var app = express(); 14 +require('express-handlebars');
  15 +
  16 +
  17 +let app = express();
14 18
15 app.set('view engine', '.hbs'); 19 app.set('view engine', '.hbs');
16 20
  21 +app.use(favicon(path.join(__dirname, '/public/favicon.ico')));
  22 +app.use(express.static(path.join(__dirname, 'public')));
17 app.use(bodyParser.json()); 23 app.use(bodyParser.json());
18 app.use(bodyParser.urlencoded({extended: false})); 24 app.use(bodyParser.urlencoded({extended: false}));
19 app.use(cookieParser()); 25 app.use(cookieParser());
20 -app.use(express.static(path.join(__dirname, 'public')));  
21 26
22 // dispatcher 27 // dispatcher
23 require('./dispatch')(app); 28 require('./dispatch')(app);
  1 +/**
  2 + * sub app guang
  3 + * @author: hbomb<qiqi.zhou@yoho.cn>
  4 + * @date: 2016/05/06
  5 + */
  6 +
  7 +var express = require('express'),
  8 + path = require('path'),
  9 + hbs = require('express-handlebars');
  10 +
  11 +var app = express();
  12 +
  13 +// set view engin
  14 +var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root
  15 +
  16 +app.set('views', path.join(__dirname, 'views/action'));
  17 +app.engine('.hbs', hbs({
  18 + extname: '.hbs',
  19 + defaultLayout: 'layout',
  20 + layoutsDir: doraemon,
  21 + partialsDir: ['./views/partial', `${doraemon}/partial`],
  22 + helpers: 'helpers'
  23 +}));
  24 +
  25 +// router
  26 +require('./router')(app);
  27 +
  28 +module.exports = app;
  1 +/**
  2 + * router of sub app guang
  3 + * @author: hbomb<qiqi.zhou@yoho.cn>
  4 + * @date: 2016/05/06
  5 + */
  6 +
  7 +'use strict';
  8 +
  9 +
  10 +let API = require('../../library/api');
  11 +
  12 +// Your controller here
  13 +let router = function(app) {
  14 + app.get('/', function(req, res) {
  15 + let api = new API();
  16 + let name = '/productColor/queryProductColors';
  17 +
  18 + api.get(name, {}).then(function(body) {
  19 + res.send(body);
  20 + }).catch(function() {
  21 + res.send('something wrong');
  22 + });
  23 + });
  24 +};
  25 +
  26 +
  27 +
  28 +
  29 +module.exports = router;
  1 +/**
  2 + * 系统配置
  3 + *
  4 + * @author hbomb qiqi.zhou@yoho.cn
  5 + * @date 2016/05/06
  6 + */
  7 +
  8 +module.exports = {
  9 + domains: {
  10 + api: 'http://192.168.102.202:8088/platform'
  11 + },
  12 + loggers: {
  13 + infoFile: {
  14 + name: 'info',
  15 + level: 'info',
  16 + filename: 'info.log'
  17 + },
  18 + errorFile: {
  19 + name: 'error',
  20 + level: 'error',
  21 + filename: 'error.log',
  22 + handleExceptions: true
  23 + },
  24 + udp: { // send by udp
  25 + level: 'debug', // logger level
  26 + host: '192.168.102.162', // influxdb host
  27 + port: '4444'// influxdb port
  28 + },
  29 + console: {
  30 + level: 'debug',
  31 + colorize: 'all',
  32 + prettyPrint: true
  33 + }
  34 + }
  35 +};
@@ -8,21 +8,64 @@ @@ -8,21 +8,64 @@
8 8
9 const rp = require('request-promise'); 9 const rp = require('request-promise');
10 const _ = require('lodash'); 10 const _ = require('lodash');
  11 +const log = require('./logger');
  12 +const api = require('../config/common').domains.api;
  13 +const Timer = require('./timer');
11 14
12 -const ApiUrl = 'http://testapi.yoho.cn:28078/'; 15 +/**
  16 + * 日志打印
  17 + * @param {Object} requestPromise 请求的promise对象
  18 + */
  19 +function logPrint(options, requestPromise) {
  20 +
  21 + // 接口统计
  22 + let timer = new Timer();
  23 +
  24 + timer.put('getApi');// 统计时间开始
  25 +
  26 + let method = options.method ? options.method : 'get';
  27 +
  28 + log.info('API Begin %s call: %s, parms: %j',
  29 + method, options.url, options.qs, {});
  30 + requestPromise.then((body) => {
  31 + let duration = timer.put('getApi');// 接口返回
  32 +
  33 + log.info('API %s res: %s, parms: %j , durationMs: %d ms , body: %s',
  34 + method, options.url, options.qs, duration, body);
  35 + }).catch((error) => {
  36 + let duration = timer.put('getApi');// 接口返回
  37 +
  38 + log.error('API %s res: %s, parms: %j , durationMs: %d ms error: %s , statusCode: %d',
  39 + method, options.url, options.qs, duration, error.message, error.statusCode);
  40 + });
  41 +}
  42 +
  43 +
  44 +let ApiUrl;
13 45
14 class API { 46 class API {
15 47
  48 + constructor(url) {
  49 + ApiUrl = url || api;
  50 + }
  51 +
16 /** 52 /**
17 * get 53 * get
18 * @param url String 54 * @param url String
19 * @param data Obejct 55 * @param data Obejct
20 */ 56 */
21 get(url, data) { 57 get(url, data) {
22 - return rp({ 58 +
  59 + let options = {
23 url: `${ApiUrl}${url}`, 60 url: `${ApiUrl}${url}`,
24 qs: data 61 qs: data
25 - }); 62 + };
  63 +
  64 + let requestPromise = rp(options);
  65 +
  66 + logPrint(options, requestPromise);// 日志打印
  67 +
  68 + return requestPromise;
26 } 69 }
27 70
28 /** 71 /**
@@ -30,13 +73,19 @@ class API { @@ -30,13 +73,19 @@ class API {
30 * @params: urls => Array[Object[url[string], data[object]]] 73 * @params: urls => Array[Object[url[string], data[object]]]
31 */ 74 */
32 multiGet(urls) { 75 multiGet(urls) {
33 - var rps = []; 76 + let rps = [];
34 77
35 _.forEach(urls, function(el) { 78 _.forEach(urls, function(el) {
36 - rps.push(rp({ 79 + let options = {
37 url: `${ApiUrl}${el.url}`, 80 url: `${ApiUrl}${el.url}`,
38 qs: el.data 81 qs: el.data
39 - })); 82 + };
  83 +
  84 + let requestPromise = rp(options);
  85 +
  86 + logPrint(options, requestPromise);// 打印日志
  87 +
  88 + rps.push(requestPromise);
40 }); 89 });
41 90
42 return Promise.all(rps).then((d) => { 91 return Promise.all(rps).then((d) => {
@@ -50,11 +99,17 @@ class API { @@ -50,11 +99,17 @@ class API {
50 * @param data Obejct 99 * @param data Obejct
51 */ 100 */
52 post(url, data) { 101 post(url, data) {
53 - return rp({ 102 + let options = {
54 url: `${ApiUrl}${url}`, 103 url: `${ApiUrl}${url}`,
55 method: 'post', 104 method: 'post',
56 form: data 105 form: data
57 - }); 106 + };
  107 +
  108 + let requestPromise = rp(options);
  109 +
  110 + logPrint(options, requestPromise);// 打印日志
  111 +
  112 + return requestPromise;
58 } 113 }
59 } 114 }
60 115
  1 +
  2 +/**
  3 + * 日志工具类
  4 + * @author: hbomb<qiqi.zhou@yoho.cn>
  5 + * @date: 2016/05/06
  6 + */
  7 + 'use strict';
  8 +
  9 + let winston = require('winston'),
  10 + config = require('../config/common'),
  11 + FileTransport = require('winston-daily-rotate-file');
  12 +
  13 + require('influxdb-winston');
  14 +
  15 + let logger = new (winston.Logger)({
  16 + transports: [
  17 + new (FileTransport)(config.loggers.infoFile),
  18 + new (FileTransport)(config.loggers.errorFile),
  19 + new (winston.transports.UdpTransport)(config.loggers.udp),
  20 + new (winston.transports.Console)(config.loggers.console)
  21 + ]
  22 + });
  23 +
  24 + module.exports = logger;
@@ -31,7 +31,7 @@ const packageSort = argument => { @@ -31,7 +31,7 @@ const packageSort = argument => {
31 } 31 }
32 32
33 return newObj; 33 return newObj;
34 -} 34 +};
35 35
36 /** 36 /**
37 * 生成签名 37 * 生成签名
@@ -46,7 +46,7 @@ const makeSign = argument => { @@ -46,7 +46,7 @@ const makeSign = argument => {
46 }); 46 });
47 47
48 return md5(qs.join('&')).toLowerCase(); 48 return md5(qs.join('&')).toLowerCase();
49 -} 49 +};
50 50
51 // 生成API签名,调用后端接口的时候有私钥校验 51 // 生成API签名,调用后端接口的时候有私钥校验
52 exports.apiSign = (params) => { 52 exports.apiSign = (params) => {
  1 +'use strict';
  2 +
  3 +/**
  4 + * 计时类
  5 + * @example
  6 + * let timer = new Timer();
  7 + * timer.put('profile');
  8 + * timer.put('proflie'); // console output: 12.14
  9 + *
  10 + * @author: hbomb<qiqi.zhou@yoho.cn>
  11 + * @date: 2016/05/07
  12 + */
  13 +
  14 +class Timer {
  15 + constructor() {
  16 + this.timers = {};
  17 + }
  18 +
  19 + /**
  20 + * 打点计时
  21 + */
  22 + put(label) {
  23 + let labelTime = this.timers[label];
  24 +
  25 + if (labelTime) {
  26 + let duration = process.hrtime(labelTime);
  27 +
  28 + return this._round(duration[1]);
  29 + } else {
  30 + this.timers[label] = process.hrtime();
  31 + }
  32 + }
  33 +
  34 + /**
  35 + * 格式化成毫秒
  36 + * @param {Number} value 纳秒
  37 + */
  38 + _round(value) {
  39 + return Math.round(value / 10000) / 100;
  40 + }
  41 +
  42 +}
  43 +
  44 +module.exports = Timer;
  1 +const shelljs = require('shelljs');
  2 +const path = require('path');
  3 +
  4 +const changeFiles = {
  5 + js: shelljs.exec('git diff --cached --name-only --diff-filter=ACM | grep .js$', {silent: true}).stdout,
  6 + css: shelljs.exec('git diff --cached --name-only --diff-filter=ACM | grep .css$', {silent: true}).stdout
  7 +};
  8 +const lintPath = {
  9 + js: path.resolve('./node_modules/.bin/eslint'),
  10 + css: path.resolve('./node_modules/.bin/stylelint')
  11 +};
  12 +const lintResult = {
  13 + js: {},
  14 + css: {}
  15 +};
  16 +
  17 +const ext = process.platform === 'win32' ? '.cmd' : ''; // Windows 平台需要加后缀
  18 +
  19 +// 在执行检查脚本的时候,不显示 NPM 错误日志
  20 +if (!shelljs.grep('npm run -s', path.resolve('./.git/hooks/pre-commit')).stdout.trim()) {
  21 + shelljs.sed('-i', 'npm run', 'npm run -s', path.resolve('./.git/hooks/pre-commit'));
  22 +}
  23 +
  24 +if (changeFiles.js) {
  25 + changeFiles.js = changeFiles.js.replace(/\n/g, ' ');
  26 + lintResult.js = shelljs.exec(`${lintPath.js}${ext} -c .eslintrc --cache --fix ${changeFiles.js}`);
  27 +}
  28 +
  29 +if (changeFiles.css) {
  30 + changeFiles.css = changeFiles.css.replace(/\n/g, ' ');
  31 + lintResult.css = shelljs.exec(`${lintPath.css}${ext} --config .stylelintrc ${changeFiles.css}`);
  32 +}
  33 +
  34 +if (lintResult.js.code || lintResult.css.code) {
  35 + process.exit(lintResult.js.code || lintResult.css.code); // eslint-disable-line
  36 +}
@@ -12,9 +12,9 @@ @@ -12,9 +12,9 @@
12 "dev": "node_modules/.bin/nodemon -e js,hbs -i public/ app.js", 12 "dev": "node_modules/.bin/nodemon -e js,hbs -i public/ app.js",
13 "online": "NODE_ENV=\"production\" node app.js", 13 "online": "NODE_ENV=\"production\" node app.js",
14 "debug": "DEBUG=\"express:*\" node app.js", 14 "debug": "DEBUG=\"express:*\" node app.js",
15 - "lint-js": "node_modules/.bin/eslint -c .eslintrc --cache --fix `git diff --cached --name-only --diff-filter=ACM | grep .js$` app.js",  
16 - "lint-css": "node_modules/.bin/stylelint --config .stylelintrc `git diff --cached --name-only --diff-filter=ACM | grep .css$`",  
17 - "precommit": "npm run lint-js && npm run lint-css" 15 + "lint-js": "./node_modules/.bin/eslint -c .eslintrc --cache --fix .",
  16 + "lint-css": "./node_modules/.bin/stylelint --config .stylelintrc public/**/*.css",
  17 + "precommit": "node lint.js"
18 }, 18 },
19 "license": "MIT", 19 "license": "MIT",
20 "dependencies": { 20 "dependencies": {
@@ -22,11 +22,14 @@ @@ -22,11 +22,14 @@
22 "cookie-parser": "^1.4.1", 22 "cookie-parser": "^1.4.1",
23 "express": "^4.13.1", 23 "express": "^4.13.1",
24 "express-handlebars": "^3.0.0", 24 "express-handlebars": "^3.0.0",
25 - "lodash": "^4.8.2", 25 + "influxdb-winston": "^1.0.1",
  26 + "lodash": "^4.12.0",
26 "md5": "^2.1.0", 27 "md5": "^2.1.0",
27 "morgan": "^1.7.0", 28 "morgan": "^1.7.0",
28 - "request-promise": "^2.0.1",  
29 - "serve-favicon": "^2.3.0" 29 + "request-promise": "^3.0.0",
  30 + "serve-favicon": "^2.3.0",
  31 + "winston": "^2.2.0",
  32 + "winston-daily-rotate-file": "^1.0.1"
30 }, 33 },
31 "devDependencies": { 34 "devDependencies": {
32 "autoprefixer": "^6.3.6", 35 "autoprefixer": "^6.3.6",
@@ -39,6 +42,8 @@ @@ -39,6 +42,8 @@
39 "gulp-sourcemaps": "^2.0.0-alpha", 42 "gulp-sourcemaps": "^2.0.0-alpha",
40 "gulp-util": "^3.0.7", 43 "gulp-util": "^3.0.7",
41 "husky": "^0.11.4", 44 "husky": "^0.11.4",
  45 + "mocha": "^2.4.5",
  46 + "nodemon": "1.9.2",
42 "postcss-assets": "^4.0.1", 47 "postcss-assets": "^4.0.1",
43 "postcss-cachebuster": "^0.1.2", 48 "postcss-cachebuster": "^0.1.2",
44 "postcss-calc": "^5.2.1", 49 "postcss-calc": "^5.2.1",
@@ -51,11 +56,12 @@ @@ -51,11 +56,12 @@
51 "postcss-sprites": "^3.1.2", 56 "postcss-sprites": "^3.1.2",
52 "postcss-use": "^2.0.2", 57 "postcss-use": "^2.0.2",
53 "precss": "^1.4.0", 58 "precss": "^1.4.0",
54 - "stylelint": "^6.2.2",  
55 - "stylelint-config-yoho": "^1.2.0", 59 + "rewire": "^2.5.1",
  60 + "shelljs": "^0.7.0",
  61 + "stylelint": "^6.3.3",
  62 + "stylelint-config-yoho": "^1.2.2",
56 "webpack": "^1.13.0", 63 "webpack": "^1.13.0",
57 "webpack-dev-server": "^1.14.1", 64 "webpack-dev-server": "^1.14.1",
58 - "webpack-stream": "^3.1.0",  
59 - "shelljs": "^0.7.0" 65 + "webpack-stream": "^3.1.0"
60 } 66 }
61 } 67 }
  1 +let expect = require('expect.js');
  2 +let Timer = require('../../library/timer');
  3 +
  4 +
  5 +describe('/library/timer', function() {
  6 + it('延迟100ms,期望大于或等于100ms', function(done) {
  7 + let t = new Timer();
  8 + t.put('aa');
  9 + setTimeout(function() {
  10 + let time = t.put('aa');
  11 + expect(Math.round(time) >= 100).to.be.ok();
  12 + done();
  13 + }, 100);
  14 + });
  15 +});
  1 +require('./library/timer.test');