lintry

init

1 +module.exports = {
2 + "env": {
3 + "es6": true,
4 + "node": true
5 + },
6 + "extends": "eslint:recommended",
7 + "parserOptions": {
8 + "ecmaVersion": 2019,
9 + "sourceType": "module"
10 + },
11 + "rules": {
12 + // "indent": [
13 + // "warn",
14 + // 4
15 + // ],
16 + "linebreak-style": "off",
17 + "quotes": "off",
18 + "semi": [
19 + "off",
20 + "always"
21 + ],
22 + "no-console": [
23 + "error", { allow: ["info", "warn", "error"]}
24 + ],
25 + "no-unused-vars": [
26 + "warn",
27 + { "vars": "all", "args": "after-used", "ignoreRestSiblings": false }
28 + ],
29 + "no-constant-condition": [
30 + "warn"
31 + ],
32 + "no-control-regex": "off",
33 + "no-redeclare": "off",
34 + "require-atomic-updates": "off",
35 + "no-prototype-builtins": 'off'
36 + }
37 +};
1 +{
2 + "esversion": 6
3 +}
...\ No newline at end of file ...\ No newline at end of file
1 +require('./server/server');
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "name": "ncov",
3 + "version": "1.0.0",
4 + "description": "",
5 + "main": "index.js",
6 + "scripts": {
7 + "test": "echo \"Error: no test specified\" && exit 1",
8 + "check": "eslint ./server --quiet"
9 + },
10 + "keywords": [],
11 + "author": "",
12 + "license": "MIT",
13 + "dependencies": {
14 + "body-parser": "^1.19.0",
15 + "connect-redis": "^4.0.3",
16 + "cookie-parser": "^1.4.4",
17 + "express": "^4.17.1",
18 + "express-session": "^1.17.0",
19 + "fs-extra": "^8.1.0",
20 + "kml-api-request": "git+ssh://git@gitlab.kmlab.com/comm/api-request.git#1.2.0",
21 + "kml-customize": "git+ssh://git@gitlab.kmlab.com/comm/customize.git#1.0.0",
22 + "kml-express-stage-lib": "git+ssh://git@gitlab.kmlab.com/comm/express-stage-lib.git#1.1.0",
23 + "kml-express-stage-mw": "git+ssh://git@gitlab.kmlab.com/comm/express-stage-mw.git#1.1.0",
24 + "kml-id-worker": "^1.0.4",
25 + "kml-oauth-app-client": "git+ssh://git@gitlab.kmlab.com/danny/kml-oauth-app-client.git#1.5.0",
26 + "kml-original-url-mw": "git+ssh://git@gitlab.kmlab.com/comm/original-url-mw.git#1.0.0",
27 + "kml-po-abstract": "git+ssh://git@gitlab.kmlab.com/entities/po-abstract.git#1.1.2",
28 + "kml-redis-tools": "git+ssh://git@gitlab.kmlab.com/comm/redis-tools.git#1.0.0",
29 + "kml-si-client": "git+ssh://git@gitlab.kmlab.com/comm/si-client.git#1.1.0",
30 + "kml-si-oauth-sdk": "git+ssh://git@gitlab.kmlab.com/saas/oauth-sdk.git#1.2.0",
31 + "kml-weapp-mw": "git+ssh://git@gitlab.kmlab.com/comm/weapp-mw.git#1.2.1",
32 + "kml-wxapi": "git+ssh://git@gitlab.kmlab.com/comm/wxapi.git#1.16.0",
33 + "log4js": "^6.1.0",
34 + "method-override": "^3.0.0",
35 + "moment": "^2.24.0",
36 + "node-cron": "^2.0.3",
37 + "pg": "^7.17.1",
38 + "redis": "^2.8.0",
39 + "request-ip": "^2.1.3",
40 + "sequelize": "^5.21.3",
41 + "shelljs": "^0.8.3"
42 + }
43 +}
1 +/**
2 + * 处理接口api的action路由
3 + */
4 +/* global global */
5 +/* global process */
6 +"use strict";
7 +
8 +function BZApiAction (routerPath) {
9 + routerPath = routerPath || '';
10 +
11 + const _ = require('lodash'),
12 + express = require('express'),
13 + router = express.Router(),
14 + authorizer = require('kml-express-stage-mw').authorizer,
15 + bz_authorizer = require('../lib/mw/bz_authorizer'),
16 + rawXML = require('kml-express-stage-mw').rawXML,
17 + execApi = require('kml-express-stage-mw').execApi,
18 + default_params = require('../lib/mw/default_params');
19 +
20 + const fn_request = function (req, res, next) {
21 + return execApi(routerPath, req, res, next);
22 + };
23 +
24 + //session中记录ip
25 + router.use(function (req, res, next) {
26 + req.session.clientIp = req.clientIp;
27 + next && next();
28 + });
29 +
30 + //api解析
31 + const mw_api = require('kml-express-stage-mw').restfulApi({routerPath: routerPath});
32 + const API_REGEXP = mw_api.regexp;
33 + router.use(mw_api.mw);
34 +
35 + //验证身份
36 + router.use(authorizer());
37 + //验证后台身份
38 + router.use(bz_authorizer());
39 + //添加default_params
40 + router.use(default_params());
41 + //解析xml
42 + router.use(rawXML());
43 +
44 +
45 + //等于各种方法的绑定,注意需要直接使用router[method],express内部绑定时需要用到this
46 + ['get', 'post', 'put', 'delete'].forEach(method => {
47 + router[method] && router[method](API_REGEXP, fn_request);
48 + });
49 +
50 +
51 + return router;
52 +}
53 +
54 +module.exports = BZApiAction;
1 +/**
2 + * 处理接口api的action路由
3 + */
4 +/* global global */
5 +/* global process */
6 +"use strict";
7 +
8 +function MpApiAction (routerPath) {
9 + routerPath = routerPath || '';
10 +
11 + const _ = require('lodash'),
12 + express = require('express'),
13 + router = express.Router(),
14 + config = global.config,
15 + authorizer = require('kml-express-stage-mw').authorizer,
16 + rawXML = require('kml-express-stage-mw').rawXML,
17 + execApi = require('kml-express-stage-mw').execApi,
18 + default_params = require('../lib/mw/default_params');
19 +
20 + const fn_request = function (req, res, next) {
21 + return execApi(routerPath, req, res, next);
22 + };
23 +
24 + //session中记录ip
25 + router.use(function (req, res, next) {
26 + req.session.clientIp = req.clientIp;
27 + next && next();
28 + });
29 +
30 + //api解析
31 + const mw_api = require('kml-express-stage-mw').restfulApi({routerPath: routerPath});
32 + const mw_oauth = require('kml-wxapi/lib/mw/oauth')({
33 + api_regexp: ['/t/wx/index'],
34 + wechat: config.wechat,
35 + server_id: config.system.project_name,
36 + wx_state: config.system.project_name
37 + });
38 + const API_REGEXP = mw_api.regexp;
39 + router.use(mw_api.mw);
40 + //解析用户微信授权
41 + router.use(mw_oauth.mw);
42 + //验证身份
43 + router.use(authorizer());
44 + //添加default_params
45 + router.use(default_params());
46 + //解析xml
47 + router.use(rawXML());
48 +
49 +
50 + //等于各种方法的绑定,注意需要直接使用router[method],express内部绑定时需要用到this
51 + ['get', 'post', 'put', 'delete'].forEach(method => {
52 + router[method] && router[method](API_REGEXP, fn_request);
53 + });
54 +
55 + return router;
56 +}
57 +
58 +module.exports = MpApiAction;
1 +/**
2 + * 处理微信小程序接口api的action路由
3 + */
4 +/* global global */
5 +/* global process */
6 +"use strict";
7 +
8 +function WeApiAction (routerPath) {
9 + routerPath = routerPath || '';
10 +
11 + const _ = require('lodash'),
12 + express = require('express'),
13 + router = express.Router(),
14 + config = global.config,
15 + mock_sim = require('kml-express-stage-mw').mockSim,
16 + authorizer = require('kml-express-stage-mw').authorizer,
17 + rawXML = require('kml-express-stage-mw').rawXML,
18 + execApi = require('kml-express-stage-mw').execApi,
19 + default_params = require('../lib/mw/default_params'),
20 + weapp_param_mw = require('../lib/mw/weapp-params');
21 +
22 + const fn_request = function (req, res, next) {
23 + return execApi(routerPath, req, res, next);
24 + };
25 +
26 + //session中记录ip
27 + router.use(function (req, res, next) {
28 + req.session.clientIp = req.clientIp;
29 + next && next();
30 + });
31 +
32 + //微信小程序访问token解析
33 + const token_parser = require('kml-weapp-mw').TokenParser({
34 + redis: require('../init/redis-promisify'),
35 + strict_mode: config.weapp.strict_mode
36 + });
37 + router.use(token_parser.mw);
38 +
39 + //处理微信小程序的参数
40 + router.use(weapp_param_mw());
41 +
42 + //api解析
43 + const mw_api = require('kml-express-stage-mw').restfulApi({routerPath: routerPath});
44 + const mw_oauth = require('kml-wxapi/lib/mw/oauth')({
45 + api_regexp: ['/t/wx/index'],
46 + wechat: config.wechat,
47 + server_id: config.system.project_name,
48 + wx_state: config.system.project_name
49 + });
50 + const API_REGEXP = mw_api.regexp;
51 + router.use(mw_api.mw);
52 + //解析用户微信授权
53 + router.use(mw_oauth.mw);
54 + //mock数据解析
55 + router.use(mock_sim());
56 + //验证身份
57 + router.use(authorizer());
58 + //添加default_params
59 + router.use(default_params());
60 + //解析xml
61 + router.use(rawXML());
62 +
63 +
64 + //等于各种方法的绑定,注意需要直接使用router[method],express内部绑定时需要用到this
65 + ['get', 'post', 'put', 'delete'].forEach(method => {
66 + router[method] && router[method](API_REGEXP, fn_request);
67 + });
68 +
69 +
70 + return router;
71 +}
72 +
73 +module.exports = WeApiAction;
1 +/**
2 + * Created by lintry on 18/12/20.
3 + */
4 +"use strict";
5 +/**
6 + * 心跳检查
7 + */
8 +const Result = require('kml-express-stage-lib').Result;
9 +
10 +module.exports = async (cfg) => {
11 + return Result.Ok(`I am alive! from ${process.pid}`);
12 +};
1 +/**
2 + * run
3 + * Created by lintry on 2020/1/28.
4 + */
5 +const config = global.config;
6 +const dbo = global.sequelize;
7 +const DataImporter = require('../service/data_importer');
8 +const data_importer = new DataImporter(dbo);
9 +const NCOV_URL = config.NCOV.API_URL;
10 +const Result = require('kml-express-stage-lib').Result;
11 +
12 +module.exports = async (cfg) => {
13 + await data_importer.import_url(NCOV_URL, {latest: 0})
14 + return Result.Ok(`Fetched from ${NCOV_URL}`);
15 +};
1 +/**
2 + * Created by lintry on 18/12/20.
3 + */
4 +"use strict";
5 +/**
6 + * 定时器定义
7 + */
8 +const fs = require('fs-extra'),
9 + path = require('path'),
10 + cron = require('node-cron'),
11 + moment = require('moment'),
12 + SCHEDULE = require('../init/config').schedule,
13 + Result = require('kml-express-stage-lib').Result,
14 + logger = require('../init/log4js-init').cron;
15 +const TTL = global.config.cache.ttl.CRON_LOCK;
16 +const lockKey = require('../modules/lockKey');
17 +const {sequelize, po} = global;
18 +const id_worker = require('../modules/id_worker');
19 +
20 +module.exports = function () {
21 + let task_list = [];
22 +
23 + // 遍历配置的定时器
24 + Object.keys(SCHEDULE).forEach(cron_id => {
25 + let cfg = SCHEDULE[cron_id];
26 +
27 + if (cfg.enable && fs.existsSync(path.resolve(__dirname, cron_id + '.js'))) {
28 + // 以键值在当前目录下加载异步执行模块
29 + const async_runner = require('./' + cron_id);
30 + if (!async_runner) {
31 + logger.error(`没有找到可以执行的模块`, cfg.name);
32 + return;
33 + }
34 + if (!cron.validate(cfg.cron)) {
35 + logger.error('定时器的时间定义错误', cfg.cron);
36 + return;
37 + }
38 + logger.info('开启定时器', cfg.name, cfg.cron);
39 + task_list.push(cron.schedule(cfg.cron, async function () {
40 + let result = await lockKey.lockKey(`cron:${cron_id}`, TTL);
41 + if (result.locked) {
42 +
43 + let result;
44 + try {
45 + result = await async_runner(cfg);
46 + } catch (e) {
47 + result = Result.Error(e.message); // 记录执行的错误
48 + }
49 +
50 + logger.info(moment().format('YYYY-MM-DD HH:mm:ss'), `${cfg.name} logger =>`, result.ret, result.msg, result.content);
51 +
52 + }
53 + }, {scheduled: false}));
54 + }
55 + });
56 +
57 + return task_list;
58 +};
1 +"use strict";
2 +const config = require('./init/config');
3 +
4 +// 定义log4js 包含业务日志和系统日志
5 +const logger = require('./init/log4js-init').system;
6 +
7 +// 定义db
8 +logger.info('init db');
9 +require('./init/sequelize-init');
10 +
11 +//bind exception event to log
12 +process.on('uncaughtException', function (e) {
13 + logger.error('uncaughtException from process', e);
14 +});
15 +
16 +process.on('unhandledRejection', (e) => {
17 + logger.warn('unhandledRejection from process', e);
18 +});
19 +
20 +process.on('rejectionHandled', (e) => {
21 + logger.warn('rejectionHandled from process', e);
22 +});
23 +
24 +// 准备定时器
25 +const redis_client = require('./init/redis-promisify'),
26 + LockKey = require('kml-redis-tools').LockKey;
27 +const lockKey = new LockKey(redis_client);
28 +
29 +lockKey.lockKey('cron:' + config.system.project_name, config.cache.ttl.CRON_LOCK)
30 + .then(lock_result => {
31 + if (lock_result.locked) {
32 + const task_list = require('./cron')();
33 + if (!task_list || !task_list.length) {
34 + logger.warn('没有定时器被开启。');
35 + }
36 + task_list.forEach(task => {
37 + task && task.start && task.start();
38 + });
39 + } else {
40 + logger.warn('定时器被锁定,请检查是否已有其他进程负责。');
41 + }
42 + });
1 +"use strict";
2 +/**
3 + * 各区域时间线
4 + */
5 +module.exports = function (sequelize, DataTypes) {
6 + const
7 + AbstractPO = require('kml-po-abstract'),
8 + _ = require('lodash');
9 +
10 + let option = AbstractPO.BaseOption({
11 + tableName: 'tb_mf_timeline_area'
12 + });
13 +
14 + let entity = _.merge({
15 + "id": {
16 + "type": DataTypes.STRING(40),
17 + "comment": "id",
18 + "field": "id",
19 + "allowNull": false,
20 + "primaryKey": true
21 + }
22 + }, AbstractPO.DefaultEntity(sequelize, DataTypes), {
23 + "province_name": {
24 + "type": DataTypes.STRING(40),
25 + "comment": "省",
26 + "field": "province_name",
27 + "allowNull": false
28 + },
29 + "province_short_name": {
30 + "type": DataTypes.STRING(40),
31 + "comment": "简称",
32 + "field": "province_short_name"
33 + },
34 + "confirmed_count": {
35 + "type": DataTypes.BIGINT,
36 + "comment": "确认人数",
37 + "field": "confirmed_count"
38 + },
39 + "suspected_count": {
40 + "type": DataTypes.BIGINT,
41 + "comment": "疑似人数",
42 + "field": "suspected_count"
43 + },
44 + "cured_count": {
45 + "type": DataTypes.BIGINT,
46 + "comment": "治愈人数",
47 + "field": "cured_count"
48 + },
49 + "dead_count": {
50 + "type": DataTypes.BIGINT,
51 + "comment": "死亡人数",
52 + "field": "dead_count"
53 + },
54 + "comment": {
55 + "type": DataTypes.STRING(200),
56 + "comment": "注释",
57 + "field": "comment"
58 + },
59 + "cities": {
60 + "type": DataTypes.JSONB,
61 + "comment": "城市",
62 + "field": "cities"
63 + },
64 + "update_time": {
65 + "type": DataTypes.DATE,
66 + "comment": "更新时间",
67 + "field": "update_time"
68 + },
69 + "country": {
70 + "type": DataTypes.STRING(40),
71 + "comment": "国家",
72 + "field": "country"
73 + }
74 + });
75 +
76 + return sequelize.define('timeline_area', entity, option);
77 +};
1 +"use strict";
2 +
3 +const configure = function () {
4 + const path = require('path'),
5 + fs = require('fs-extra'),
6 + chalk = require('chalk'),
7 + customize = require('kml-customize'),
8 + PROJECT_NAME = 'ncov';
9 +
10 + let BASE_URL = `https://dev.kmlab.com/${PROJECT_NAME}/`;
11 + let host = '58.246.253.126';
12 + let port = '5433';
13 + let database = 'jhsaas';
14 + let username = 'pms';
15 + let password = 'pms';
16 + let redisHost = '192.168.1.90';
17 + let redisPort = 6379;
18 + let redisDB = 0;
19 + let redisPass = 'itomix';
20 +
21 + const root = process.cwd(),
22 + server_path = 'server', //后端主目录
23 + init_path = 'init', //后端初始化目录
24 + lib_path = 'lib', //后端自定义库
25 + modules_path = 'modules', //后端模块定义
26 + routers_path = 'routers', //后端路由定义
27 + entities_path = 'entities', //实体类定义
28 + public_path = 'public', //静态资源目录
29 + logs_path = 'logs', //日志目录
30 + views_path = 'views', //前端视图输出
31 + mock_path = 'mock' //mock数据目录
32 + ;
33 +
34 + let config = {
35 + //sequelize数据库连接定义
36 + sequelize: {
37 + database: database,
38 + username: username,
39 + password: password,
40 + options: {
41 + timezone: '+08:00', //保证时区正确
42 + host: host,
43 + port: port,
44 + dialect: 'postgres',
45 + //storage: 'path/to/database.sqlite', //SQLite only
46 + pool: {
47 + max: 5,
48 + min: 0,
49 + idle: 10000
50 + }
51 + },
52 + syncDB: false
53 + },
54 + redis: {
55 + host: redisHost,
56 + port: redisPort,
57 + db: redisDB,
58 + pass: redisPass
59 + },
60 +
61 + //系统目录定义
62 + system: {
63 + bind_port: 9120, //绑定端口
64 + base_url: BASE_URL, //主URL
65 + project_name: PROJECT_NAME, //项目名
66 + additional: '_action', //路由后缀
67 + mock_file: '.js', //对应url请求时获取mock数据类型的文件后缀
68 + mock_mode: process.env.MOCK_MODE || false, //mock模式, auto=自动|true|false
69 + real_mode: /^product|real$/.test(process.env.NODE_ENV) //连接真实生产环境
70 + },
71 +
72 + //系统路径
73 + path: {
74 + ROUTERS_PATH: path.resolve(root, server_path, routers_path), //后端路由定义
75 + ENTITIES_PATH: path.resolve(root, server_path, entities_path), //实体类定义
76 + INIT_PATH: path.resolve(root, server_path, init_path), //后端初始化目录,固定
77 + LIB_PATH: path.resolve(root, server_path, lib_path), //后端自定义库,固定
78 + MODULES_PATH: path.resolve(root, server_path, modules_path), //后端模块定义
79 + MOCK_PATH: path.resolve(root, mock_path), ///mock数据目录
80 + PUBLIC_PATH: path.resolve(root, public_path), //静态资源目录
81 + VIEWS_PATH: path.resolve(root, views_path), //前端视图输出
82 + LOGS_PATH: path.resolve(root, logs_path) //日志目录
83 + },
84 +
85 + //文件上传目录定义
86 + upload: {
87 + root: `/opt/${PROJECT_NAME}-img/`,
88 + base_url: null,
89 + appimg: 'appimg/',
90 + qrimg: 'qrimg/',
91 + wximg: 'wximg/'
92 + },
93 +
94 + //微信定义
95 + wechat: {
96 + api_host: 'http://localhost:3800',
97 + wx_app: 'itomix',
98 + api_token: '',
99 + authorizer_appid: ''
100 + },
101 +
102 + //微信小程序
103 + weapp: {
104 + api_host: 'http://localhost:3800',
105 + api_token: '',
106 + authorizer_appid: '',
107 + watermark_ttl: 7200, //敏感数据有效期(s)
108 + token_prefix: 'WXID', //token前缀
109 + token_ttl: 36000, //token有效期(s)
110 + share_prefix: 'WXSHARE', //分享前缀
111 + share_ttl: 3600 * 48, //分享有效期(s)
112 + strict_mode: true
113 + },
114 +
115 + //内部应用接口验证定义
116 + app_config: {
117 + api_token: ''
118 + },
119 +
120 + //ID生成器
121 + id_worker: {
122 + datacenterId: 9
123 + },
124 +
125 + //枚举参数
126 + ENUM: {
127 + DEFAULT: {
128 + USERTYPE: 'P',
129 + USERPASSWORD: '8888',
130 + USERROLE: {
131 + ADMIN: 'ADMIN',
132 + USER: 'USER'
133 + },
134 + DUTY: '管理员',
135 + PLATTYPE: 'P',
136 + DUTYNAME: '管理员',
137 + CORPTYPE: 'P',
138 + DUTYTYPE: 'P'
139 + },
140 + TYPE: {
141 + APPLY: 'APPLY',
142 + ENABLE: 'ENABLE',
143 + FAIL: 'FAIL',
144 + IGNORE: 'IGNORE',
145 + DISABLE: 'DISABLE',
146 + USE: 'USE',
147 + USER: 'USER',
148 + AUTO: 'AUTO',
149 + DEFAULT: 'DEFAULT'
150 + },
151 + STATE: {
152 + SUCCESS: 'SUCCESS',
153 + OK: 'OK',
154 + ERROR: 'ERROR'
155 + },
156 + PLATTYPE: {
157 + P: 'P',
158 + B: 'B'
159 + },
160 + LIMIT: 20,
161 + OFFSET: 0,
162 + DEFAULT_LIMIT: 999,
163 + DEFAULT_PARAMS_ARRAY: ['create_id', 'create_name', 'create_code', 'create_date',
164 + 'optr_id', 'optr_name', 'optr_code', 'optr_date'],
165 + STATUS_ARRAY: ['APPLY', 'ENABLE', 'DISABLE', 'Y', 'N', 'PUBLIC', 'PRIVATE']
166 + },
167 +
168 + // 微信跳转页
169 + mpurl_map: {
170 + index: 'index.html'
171 + },
172 +
173 + // 缓存设定
174 + cache: {
175 + ttl: {
176 + PIN_TIMEOUT: 300, // 5分钟的验证码超时时间
177 + AGAIN_PIN: 240, // 4分钟再次发送验证码
178 + EXPRESS_TTL: 86400, // 1 day
179 + SESSION_TTL: 86400, // 1 day
180 + CRON_LOCK: 5
181 + }
182 + },
183 +
184 + // 定时器
185 + "cron_helper": `
186 + ┌────────────── second (optional)
187 + │ ┌──────────── minute
188 + │ │ ┌────────── hour
189 + │ │ │ ┌──────── day of month
190 + │ │ │ │ ┌────── month
191 + │ │ │ │ │ ┌──── day of week
192 + │ │ │ │ │ │
193 + │ │ │ │ │ │
194 + * * * * * *
195 + ss mm H D M dw
196 + `,
197 + "unique_schedule": false, // 独立运行定时器任务
198 + "schedule": {
199 + "alive": {
200 + "name": "心跳检查",
201 + "cron": "0-59/1 * * * * *",
202 + "enable": false
203 + },
204 + "crawler": {
205 + "name": "nCov数据抓取",
206 + "cron": "0-59/10 * * * * *",
207 + "enable": false
208 + },
209 + },
210 +
211 + // 获取数据的api地址
212 + NCOV: {
213 + API_URL: 'http://lab.isaaclin.cn/nCoV/api/area'
214 + }
215 + };
216 +
217 + //创建目录
218 + let config_path = config.path;
219 + for (let p in config_path) {
220 + if (config_path.hasOwnProperty(p)) {
221 + let path = config_path[p];
222 + fs.ensureDir(path, function (err, added_root) {
223 + if (err) {
224 + return console.error(chalk.red('create folder error'), chalk.red(JSON.stringify(err, null, 4)));
225 + }
226 + added_root && console.info(chalk.green(path + ' is created'));
227 + });
228 + }
229 + }
230 + return customize(config);
231 +}();
232 +
233 +//绑定到全局变量
234 +global.config = global.config = configure;
235 +module.exports = configure;
1 +/**
2 + * Created by lintry on 2017/4/27.
3 + */
4 +"use strict";
5 +/**
6 + * 定义实体类的关联关系
7 + * @return {boolean}
8 + */
9 +module.exports = function (sequelize) {
10 + const logger = global.loggers.system;
11 + const AbstractPO = require('kml-po-abstract');
12 +
13 + const po = new AbstractPO('../entities', __dirname);
14 +
15 + const print_entities = function (po) {
16 + po.forEach((entity) => {
17 + logger.info(`loading [${entity}]`);
18 + });
19 + };
20 +
21 + //加载所有的实体
22 + print_entities(po.import(sequelize));
23 +
24 +
25 + // todo 定义实体间的弱关联关系 {constraints: false},或者在include时定义{association}
26 +
27 + return po;
28 +};
1 +/**
2 + * express初始化
3 + */
4 +"use strict";
5 +module.exports = function () {
6 + let _ = require('lodash'),
7 + shell = require('shelljs'),
8 + express = require('express'),
9 + session = require('express-session'),
10 + app = express(),
11 + cookieParser = require('cookie-parser'),
12 + requestIp = require('request-ip'),
13 + bodyParser = require('body-parser'),
14 + methodOverride = require('method-override'),
15 + original_url_mw = require('kml-original-url-mw'),
16 + config = require('./config'),
17 + config_path = config.path,
18 + logger = require('./log4js-init').system;
19 +
20 + // 定义express body 中间件
21 + app.use(bodyParser.urlencoded({extended: false}));
22 + app.use(bodyParser.json());
23 + // app.use(bodyParser.raw({type: 'application/xml'}));
24 + app.use(methodOverride());
25 + app.use(requestIp.mw());
26 +
27 + // 加载用于解析 cookie 的中间件
28 + app.use(cookieParser());
29 +
30 + // 解析原始请求url
31 + app.use(original_url_mw());
32 +
33 + const cfg_redis = config.redis;
34 + if (cfg_redis && cfg_redis.host) {
35 + let rs = shell.exec(`ping -c 3 -t 5 ${cfg_redis.host}`), sessionMiddleware;
36 +
37 + let session_cfg = {
38 + name: config.system.project_name + '.sid',
39 + secret: 'my_secret_treasure',
40 + resave: true,
41 + saveUninitialized: false,
42 + proxy: true
43 + };
44 +
45 + if (rs.code === 0) {
46 + // 使用redis存储session
47 + let RedisStore = require('connect-redis')(session);
48 +
49 + let client = require('./redis-promisify');
50 + client.on('error', logger.error);
51 +
52 + sessionMiddleware = session(_.merge({
53 + store: new RedisStore({client, ttl: config.cache.ttl.SESSION_TTL})
54 + }, session_cfg));
55 + } else {
56 + logger.warn(`${cfg_redis.host} can not be connected, we will use default session instead!`);
57 + sessionMiddleware = session(session_cfg);
58 + }
59 +
60 + app.use(function (req, res, next) {
61 + let tries = 3;
62 +
63 + function lookupSession (error) {
64 + if (error) {
65 + return next(error);
66 + }
67 +
68 + tries--;
69 + if (req.session !== undefined) {
70 + return next();
71 + }
72 +
73 + if (tries < 0) {
74 + return next(new Error('Oops! @_@'));
75 + }
76 + sessionMiddleware(req, res, lookupSession);
77 + }
78 +
79 + lookupSession();
80 + })
81 + }
82 +
83 + // 指定一个虚拟路径static挂载静态资源
84 + app.use('/', express.static(config_path.PUBLIC_PATH));
85 +
86 + return app;
87 +};
1 +/**
2 + * Created by lintry on 17/7/18.
3 + * log4js 2.x 初始化定义
4 + */
5 +"use strict";
6 +const loggers = function () {
7 + const semver = require('semver'),
8 + _ = require('lodash'),
9 + log4js = require('log4js'),
10 + fs = require('fs-extra'),
11 + path = require('path'),
12 + config = require('./config'),
13 + config_path = config.path,
14 + project_name = config.system.project_name;
15 +
16 + const log_path = config_path.LOGS_PATH;
17 + fs.ensureDirSync(log_path);
18 +
19 + const pkg = require(path.resolve(process.cwd(), 'node_modules/log4js/package.json')),
20 + log4js_2_x = semver.gt(pkg.version, '2.0.0');
21 +
22 + const appenders = [
23 + {type: 'console'},
24 + {
25 + type: 'file',
26 + filename: path.resolve(log_path, `${project_name}.log`),
27 + maxLogSize: 20480000,
28 + backups: 30,
29 + category: 'project',
30 + level: 'WARN'
31 + },
32 + {
33 + type: 'file',
34 + filename: path.resolve(log_path, 'system.log'),
35 + maxLogSize: 20480000,
36 + backups: 30,
37 + category: 'system',
38 + level: 'ALL'
39 + },
40 + {
41 + type: 'file',
42 + filename: path.resolve(log_path, 'upload.log'),
43 + maxLogSize: 20480000,
44 + backups: 30,
45 + category: 'upload',
46 + level: 'ERROR'
47 + },
48 + {
49 + type: 'file',
50 + filename: path.resolve(log_path, 'pay.log'),
51 + maxLogSize: 20480000,
52 + backups: 30,
53 + category: 'pay',
54 + level: 'ALL'
55 + },
56 + {
57 + type: 'file',
58 + filename: path.resolve(log_path, 'wx.log'),
59 + maxLogSize: 20480000,
60 + backups: 30,
61 + category: 'wx',
62 + level: 'ALL'
63 + },
64 + {
65 + type: 'file',
66 + filename: path.resolve(log_path, 'cron.log'),
67 + maxLogSize: 20480000,
68 + backups: 30,
69 + category: 'cron',
70 + level: 'ALL'
71 + }
72 + ];
73 +
74 + if (log4js_2_x) {
75 + const _appenders = {},
76 + _categories = {};
77 +
78 + appenders.forEach(function (appender) {
79 + let name = appender.category || 'default';
80 + _appenders[name] = _.omit(appender, ['category', 'level']);
81 + _categories[name] = { appenders: [name, 'default'], level: appender.level || 'ALL'};
82 + });
83 +
84 + log4js.configure({
85 + appenders: _appenders,
86 + categories: _categories,
87 + replaceConsole: true,
88 + pm2: true
89 + });
90 + } else {
91 + log4js.configure({
92 + appenders: appenders
93 + });
94 + }
95 +
96 + const logger_export = {};
97 + appenders.forEach(function (appender) {
98 + let name = appender.category;
99 + if (name) {
100 + let logger = log4js.getLogger(name);
101 + logger_export[name] = logger;
102 + !log4js_2_x && logger.setLevel(appender.level || 'ALL');
103 + logger.trace('TRACE is enabled now!');
104 + logger.debug('DEBUG is enabled now!');
105 + logger.info('INFO is enabled now!');
106 + logger.warn('WARN is enabled now!');
107 + logger.error('ERROR is enabled now!');
108 + logger.fatal('FATAL is enabled now!');
109 + }
110 + });
111 +
112 + return logger_export;
113 +}();
114 +
115 +//绑定到全局变量
116 +global.loggers = global.loggers || loggers;
117 +module.exports = loggers;
1 +/**
2 + * Created by lintry on 2016/12/21.
3 + */
4 +"use strict";
5 +
6 +const Promise = require('bluebird'),
7 + redis = require('redis'),
8 + config = require('./config'),
9 + cfg_redis = config.redis,
10 + client = redis.createClient({
11 + host: cfg_redis.host,
12 + port: cfg_redis.port,
13 + password: cfg_redis.pass,
14 + db: cfg_redis.db,
15 + prefix: config.system.project_name + ':',
16 + socket_keepalive: true,
17 + retry_unfulfilled_commands: true
18 + });
19 +Promise.promisifyAll(redis.RedisClient.prototype);
20 +Promise.promisifyAll(redis.Multi.prototype);
21 +
22 +module.exports = client;
...\ No newline at end of file ...\ No newline at end of file
1 +/**
2 + * Sequelize初始化
3 + */
4 +"use strict";
5 +global.sequelize = global.sequelize = function () {
6 + const Sequelize = require('sequelize'),
7 + path = require('path'),
8 + logger = require('./log4js-init').system;
9 +
10 + const config = require('./config'), config_sequelize = config.sequelize;
11 +
12 + //custom method defined here
13 + config_sequelize.options.define = {
14 + classMethods: {
15 + /**
16 + * 根据主键查找并保存
17 + * @param entityRequest
18 + * @param options
19 + * @returns {Promise.<T>}
20 + */
21 + saveById: function (entityRequest, options) {
22 + let Entity = this;
23 + entityRequest = entityRequest || {};
24 +
25 + //根据主键查找是否存在
26 + let primaryKey = Entity.primaryKeyAttribute;
27 + return Entity.findById(entityRequest[primaryKey])
28 + .then(function (entity) {
29 + if (entity) { //to update
30 + return entity.update(entityRequest, options);
31 + } else { //to create
32 + return Entity.create(entityRequest, options);
33 + }
34 + })
35 + .catch(function (e) {
36 + logger.error('Class saveById', e);
37 + throw e;
38 + });
39 + }
40 + },
41 + instanceMethods: {
42 + /**
43 + * 根据主键查找并保存
44 + * @param options
45 + * @returns {Promise.<T>}
46 + */
47 + saveById: function (options) {
48 + let entityRequest = this.toJSON();
49 + let Entity = this.Model;
50 + if (!Entity) {
51 + throw new Error('wrong method caller');
52 + }
53 + //根据主键查找是否存在
54 + let primaryKey = Entity.primaryKeyAttribute;
55 + return Entity.findById(entityRequest[primaryKey])
56 + .then(function (entity) {
57 + if (entity) { //to update
58 + return entity.update(entityRequest, options);
59 + } else { //to create
60 + return Entity.create(entityRequest, options);
61 + }
62 + })
63 + .catch(function (e) {
64 + logger.error('saveById', e);
65 + throw e;
66 + });
67 + }
68 + }
69 + };
70 + const sequelize = new Sequelize(config_sequelize.database, config_sequelize.username, config_sequelize.password, config_sequelize.options);
71 +
72 + //加载表
73 + const entitis_init = require('./entities-init');
74 + global.po = entitis_init(sequelize);
75 +
76 + if (config_sequelize.syncDB) {
77 + //同步表结构db
78 + logger.info('sync db');
79 + sequelize.sync().then(function () {
80 + logger.info('db init finished!');
81 + });
82 + } else {
83 + logger.info('db init finished!');
84 + }
85 +
86 + // 兼容4.0模式,添加属性
87 + sequelize.Op = Sequelize.Op;
88 + sequelize.Transaction = Sequelize.Transaction;
89 + sequelize.Model = Sequelize.Model;
90 + sequelize.Utils = Sequelize.Utils;
91 +
92 + return sequelize;
93 +}();
94 +
95 +module.exports = global.sequelize;
1 +/**
2 + * B端后台管理身份验证
3 + * Created by lintry on 2018/06/29.
4 + */
5 +"use strict";
6 +module.exports = function () {
7 + const Result = require('kml-express-stage-lib').Result,
8 + AuthAction = require('../../modules/authAction'),
9 + db = global.sequelize,
10 + logger = global.loggers.system;
11 +
12 + return function (req, res, next) {
13 + let apiParams = req.apiParams;
14 + if (!apiParams) {
15 + return next();
16 + }
17 + let user = req.session.active_user;
18 + let action = apiParams.action;
19 + let routerPath = apiParams.routerPath;
20 + const method = apiParams.method;
21 + let needVerify = need_verify(action, routerPath, method);
22 + const userType = (user && user.user_type) ? user.user_type.toUpperCase() : '';
23 + const urlType = routerPath.toUpperCase();
24 +
25 + if (!needVerify || (userType === urlType) || userType === 'P' || urlType === 'U') {
26 + next && next();
27 + } else {
28 + if (!user) {
29 + let authAction = new AuthAction(userType);
30 + return authAction.quickPass(req, res, db)
31 + .then(function (result) {
32 + if ('OK' !== result.ret) { //登录不成功
33 + res.json(result);
34 + } else {
35 + const tempUserType = (result.content && result.content.user.user_type)
36 + ? result.content.user.user_type.toUpperCase()
37 + : '';
38 +
39 + if (tempUserType === urlType || tempUserType === 'P') {
40 + return next && next();
41 + }
42 + logger.error(`{error: '用户身份不匹配', tempUserType: ${tempUserType},urlType: ${urlType} `);
43 + return res.status(401).json(new Result(Result.ERROR, '禁止访问', '用户身份不匹配'));
44 + }
45 + })
46 + .catch(err => {
47 + logger.error('error', err);
48 + res.status(401).json(new Result(Result.ERROR, '登录失败', '', err.message));
49 + });
50 + }
51 + return res.status(401).json(new Result(Result.ERROR, '禁止访问', '用户身份不匹配'));
52 + }
53 + }
54 +};
55 +
56 +/**
57 + * 验证用户操作是否需要验证权限
58 + * @param action
59 + * @param routerPath
60 + * @param method
61 + */
62 +function need_verify (action, routerPath, method) {
63 + routerPath = routerPath.toLowerCase();
64 + let needVerify = true;
65 +
66 + if (/^[tuo]$/.test(routerPath)) { //u、t、o的公共模块直接通过
67 + needVerify = false;
68 + } else if (action === 'auth') { //登录模块直接通过
69 + needVerify = false;
70 + }
71 + return needVerify;
72 +}
73 +
1 +/**
2 + * 文件描述:
3 + * 开发者:yujintang
4 + * 开发者备注:
5 + * 审阅者:
6 + * 优化建议:
7 + * 开发时间: 2017/11/27
8 + */
9 +"use strict";
10 +
11 +const _ = require('lodash');
12 +module.exports = function () {
13 + return function (req, res, next) {
14 + let user = _.get(req, 'session.active_user', {});
15 +
16 + let {user_id, user_code, user_name} = user;
17 + req.default_params = {
18 + createdBy: {
19 + create_id: user_id,
20 + create_code: user_code,
21 + create_name: user_name,
22 + },
23 + updatedBy: {
24 + optr_id: user_id,
25 + optr_code: user_code,
26 + optr_name: user_name,
27 + }
28 + };
29 + next && next();
30 + };
31 +};
...\ No newline at end of file ...\ No newline at end of file
1 +/**
2 + * 微信小程序接口传递业务身份
3 + * Created by lintry on 2018/4/25.
4 + */
5 +
6 +module.exports = function () {
7 + return function (req, res, next) {
8 + const ENUM = global.config.ENUM
9 +
10 + let plat_id = req.header(ENUM.SHOP.we_plat_id_key) || ENUM.SHOP.plat_id; // 当前小程序的商户
11 + req.app_identity = {plat_id};
12 +
13 + next();
14 + }
15 +}
...\ No newline at end of file ...\ No newline at end of file
1 +/**
2 + * Created by lintry on 2017-5-8.
3 + */
4 +
5 +"use strict";
6 +
7 +module.exports = function (user_type, dbo) {
8 + if (user_type === void 0) {
9 + throw new Error('请明确指定登录平台');
10 + }
11 +
12 + //api公共模块
13 + const _ = require('lodash'),
14 + Promise = require('bluebird'),
15 + po = global.po,
16 + config = global.config,
17 + Result = require('kml-express-stage-lib').Result,
18 + logger = global.loggers.system,
19 + ENUM = config.ENUM,
20 + cache_config = config.cache,
21 + crypto_utils = require('kml-crypto-utils'),
22 + redisDb = require('../init/redis-promisify');
23 +
24 + const ExpressPassport = require('kml-common-module').ExpressPassport,
25 + express_passport = new ExpressPassport({
26 + server_id: global.config.system.project_name,
27 + ttl: cache_config.EXPRESS_TTL,
28 + redis_client: redisDb
29 + });
30 +
31 +
32 + /**
33 + * 登录系统
34 + * @param req
35 + * @param res
36 + * @param db
37 + * @return {*}
38 + * 仅供调试用
39 + */
40 + this.sysloginPost = async function (req, res, db) {
41 + const params = req.body;
42 +
43 + if (!params || !params.userCode || !params.userPassword) return Result.Error('参数错误');
44 +
45 + const sendData = {
46 + userCode: params.userCode,
47 + userPassword: params.userPassword+'@'+params.userCode
48 + };
49 +
50 + try {
51 + const
52 + User = po.import(db, 'user'),
53 +
54 + userWhereObj = {
55 + user_code: sendData.userCode,
56 + status: ENUM.TYPE.ENABLE
57 + };
58 +
59 + const
60 + userInfoArray = _.difference(Object.keys(User.fieldRawAttributesMap), ENUM.DEFAULT_PARAMS_ARRAY);
61 +
62 + const userInfo = await User.findOne({where: userWhereObj, attributes: userInfoArray});
63 + if (!userInfo) return Result.Error('账号或密码错误');
64 +
65 + // 检查密码编码后是否与数据库存储的编码一致
66 + let matched = crypto_utils.hashMatch('md5', sendData.userPassword, userInfo.user_password);
67 + if (!matched) {
68 + return Result.Error('账号或密码错误');
69 + }
70 +
71 + sendData.platId = userInfo.plat_id;
72 + sendData.userType = userInfo.user_type;
73 +
74 + const data = {
75 + user: userInfo.toJSON()
76 + };
77 +
78 + //屏蔽用户密码输出
79 + data.user.user_password = void 0;
80 +
81 + return express_passport.create(req, res, data).then((express_data) => {
82 + return Result.Ok('登录成功', data);
83 + });
84 +
85 + } catch(err) {
86 + logger.error('error', err);
87 + return Result.Error('登录失败');
88 + }
89 + };
90 +
91 +
92 + /**
93 + * 注销
94 + * @param req
95 + * @param res
96 + */
97 + this.logoutGet = async function (req, res) {
98 + const active_user = req.session.active_user;
99 + await express_passport.destroy(req, res);
100 +
101 + if (!active_user) {
102 + req.session.destroy();
103 + return Result.Ok('注销成功');
104 + }
105 + req.session.destroy();
106 +
107 + const key = `${active_user.user_id}@${active_user.plat_id}`;
108 + return redisDb
109 + .DELAsync(key)
110 + .then(() => {
111 + return Result.Ok('注销成功');
112 + })
113 + .catch(err => {
114 + logger.error(`${req.baseUrl}${req.url} => `, err);
115 + return Result.Error('注销成功');
116 + });
117 + };
118 +
119 + /**
120 + * 快速登录 不分平台直接登录
121 + * @param req
122 + * @param res
123 + * @returns {Promise.<T>}
124 + */
125 + this.quickPass = function (req, res, db) {
126 + return express_passport.validate(req, res)
127 + .then(async function (express_data) {
128 + if (express_data) {
129 + const
130 + User = po.import(db, 'user'),
131 +
132 + userWhereObj = {
133 + user_id: express_data.user_id
134 + };
135 +
136 + const
137 + userInfoArray = _.difference(Object.keys(User.fieldRawAttributesMap), ENUM.DEFAULT_PARAMS_ARRAY);
138 +
139 + const userInfo = await User.findOne({where: userWhereObj, attributes: userInfoArray});
140 + if (!userInfo) return Result.Error('登录信息已失效, 请重新登录!');
141 +
142 + const data = {
143 + user: userInfo.toJSON()
144 + };
145 +
146 + //屏蔽用户密码输出
147 + data.user.user_password = void 0;
148 +
149 + return express_passport.refresh(req, res, data).then(() => {
150 + return Result.Ok('登录成功', data);
151 + });
152 +
153 + } else {
154 + res.status(401);
155 + return Result.Error('登录信息已失效, 请重新登录!');
156 + }
157 + });
158 + };
159 +
160 +};
1 +/**
2 + * id_worker
3 + * Created by lintry on 2018/8/23.
4 + */
5 +
6 +const IdWorker = require('kml-id-worker'),
7 + id_worker = new IdWorker({datacenterId: global.config.id_worker.datacenterId, workerId: process.pid & 0x1f});
8 +
9 +module.exports = id_worker;
...\ No newline at end of file ...\ No newline at end of file
1 +/**
2 + * lockKey
3 + * Created by lintry on 2018/12/20.
4 + */
5 +
6 +const redis_client = require('../init/redis-promisify'),
7 + LockKey = require('kml-redis-tools').LockKey;
8 +
9 +
10 +module.exports = new LockKey(redis_client);
...\ No newline at end of file ...\ No newline at end of file
1 +"use strict";
2 +
3 +/**
4 + * @module b/auth
5 + * @author 余金堂
6 + * @description
7 + * 1.登录系统
8 + * 2.注销
9 + * 3.获取个人信息
10 + */
11 +module.exports = function ( dbo ) {
12 +
13 + //api公共模块
14 + const _ = require('lodash'),
15 + po = global.po,
16 + Result = require('kml-express-stage-lib').Result,
17 + logger = global.loggers.system,
18 + redisDb = require('../../init/redis-promisify');
19 +
20 +
21 +
22 + const authAction = new (require('../../modules/authAction'))('B', dbo);
23 +
24 + /**
25 + * 1. 登录系统
26 + *
27 + * @deprecated saas_back 平台提供方法优先
28 + * @param {Object} req - 请求参数
29 + * @param {string} req.userCode - 用户code
30 + * @param {string} req.userPassword - 密码为🔐的内容
31 + *
32 + * @param {Object} res - 返回参数
33 + * @param {string} res.ret - 返回状态 [OK、ERROR]
34 + * @param {string} res.msg - 返回消息
35 + * @param {object} res.content - 返回内容
36 + * @param {Object} res.content.plat - 平台
37 + * @param {Object} res.content.corp - 登录人所属公司
38 + * @param {Object} res.content.active_user - 个人信息
39 + * @param {Object} res.content.duty - 指责
40 + */
41 + this.sysloginPost = authAction.sysloginPost;
42 +
43 + /**
44 + * 2. 注销
45 + *
46 + * @deprecated saas_back 平台提供方法优先
47 + * @param {Object} req - 请求参数
48 + *
49 + * @param {Object} res - 返回参数
50 + * @param {object} res.content - 返回内容
51 + * @param {string} res.ret - 返回状态 [OK、ERROR]
52 + * @param {string} res.msg - 返回消息
53 + */
54 + this.logoutGet = authAction.logoutGet;
55 +
56 +
57 + /**
58 + * 3.获取个人信息
59 + *
60 + * @deprecated saas_back 平台提供方法优先
61 + * @param {Object} req - 请求参数
62 + *
63 + * @param {Object} res - 返回参数
64 + * @param {string} res.ret - 返回状态 [OK、ERROR]
65 + * @param {string} res.msg - 返回消息
66 + * @param {object} res.content - 返回内容
67 + * @param {Object} res.content.plat - 平台
68 + * @param {Object} res.content.corp - 登录人所属公司
69 + * @param {Object} res.content.active_user -个人信息
70 + * @param {Object} res.content.duty - 指责
71 + * @param {Object} res.content.saas_host - 平台主页面
72 + */
73 + this.getUserInfoGet = async (req) => {
74 + try {
75 + let session = req.session,
76 + {active_user, plat, corp, duty} = session;
77 +
78 + const data = {
79 + active_user: active_user || {},
80 + plat: plat || {},
81 + corp: corp || {},
82 + duty: duty || {}
83 + };
84 +
85 + return Result.Ok('成功!', data);
86 + } catch (e) {
87 + logger.error('失败!', e);
88 + return Result.Error('失败!', e.message);
89 + }
90 + }
91 +};
...\ No newline at end of file ...\ No newline at end of file
1 +"use strict";
2 +
3 +/**
4 + * @module b/auth
5 + * @author 余金堂
6 + * @description
7 + * 1.登录系统
8 + * 2.注销
9 + * 3.获取个人信息
10 + */
11 +module.exports = function ( dbo ) {
12 +
13 + //api公共模块
14 + const _ = require('lodash'),
15 + po = global.po,
16 + Result = require('kml-express-stage-lib').Result,
17 + logger = global.loggers.system,
18 + redisDb = require('../../init/redis-promisify');
19 +
20 +
21 + /**
22 + * 3.获取个人信息
23 + *
24 + * @deprecated saas_back 平台提供方法优先
25 + * @param {Object} req - 请求参数
26 + *
27 + * @param {Object} res - 返回参数
28 + * @param {string} res.ret - 返回状态 [OK、ERROR]
29 + * @param {string} res.msg - 返回消息
30 + * @param {object} res.content - 返回内容
31 + * @param {Object} res.content.plat - 平台
32 + * @param {Object} res.content.corp - 登录人所属公司
33 + * @param {Object} res.content.active_user -个人信息
34 + * @param {Object} res.content.duty - 指责
35 + * @param {Object} res.content.saas_host - 平台主页面
36 + */
37 + this.getUserInfoGet = async (req) => {
38 + try {
39 + let session = req.session,
40 + {active_user, plat, corp, duty} = session;
41 +
42 + const data = {
43 + active_user: active_user || {},
44 + plat: plat || {},
45 + corp: corp || {},
46 + duty: duty || {}
47 + };
48 +
49 + return Result.Ok('成功!', data);
50 + } catch (e) {
51 + logger.error('失败!', e);
52 + return Result.Error('失败!', e.message);
53 + }
54 + }
55 +};
...\ No newline at end of file ...\ No newline at end of file
1 +/**
2 + * 微信小程序服务
3 + * Created by lintry on 2018/1/18.
4 + */
5 +
6 +module.exports = function (dbo) {
7 + const _ = require('lodash'),
8 + Promise = require('bluebird'),
9 + url_utils = require('url'),
10 + config = global.config,
11 + po = global.po,
12 + Result = require('kml-express-stage-lib').Result,
13 + logger = global.loggers.system,
14 + weapp_cfg = config.weapp,
15 + redis = require('../../init/redis-promisify'),
16 + crypto_utils = require('kml-crypto-utils')
17 + ;
18 +
19 + const WeAppAction = require('kml-weapp-lib').WeAppAction;
20 + const WxTokenStore = require('kml-weapp-mw').WxTokenStore;
21 +
22 + /**
23 + * 根据header获取对应wxapi,并初始化WeAppAction模块
24 + * @param req
25 + * @return {*}
26 + */
27 + function getWeAppAction (req) {
28 + let we_app = req.header('we-app')
29 + return new WeAppAction({
30 + redis,
31 + weapp_cfg: _.merge({}, weapp_cfg, {wx_app: we_app}), //使用客户端传入的wx_app,默认使用配置中定义
32 + logger
33 + })
34 + }
35 +
36 + /**
37 + * 根据header获取对应wxapi
38 + * @param req
39 + */
40 + function getWXApi (req) {
41 + let we_app = req.header('we-app')
42 + return require('kml-wxapi')(_.merge({}, weapp_cfg, {wx_app: we_app})) //使用客户端传入的wx_app,默认使用配置中定义
43 + }
44 +
45 + /**
46 + * 检查token是否有效
47 + * @param req
48 + * @param res
49 + * @return {*}
50 + */
51 + this.checkGet = function (req, res) {
52 + return getWeAppAction(req).check(req.app_token)
53 + };
54 +
55 +
56 + /**
57 + * 登录凭证code换取session_key
58 + * @param req
59 + * @param res
60 + */
61 + this.loginPost = async function (req, res) {
62 + return getWeAppAction(req).login(req.app_token, req.body);
63 + };
64 +
65 +
66 + /**
67 + * 分享
68 + * @param req
69 + * @param res
70 + * 每次分享都会记录下openid分享什么内容到哪个组,以分享票据作为唯一键值
71 + */
72 + this.sharePost = async function (req, res) {
73 + let {appid, session_key, openid, version} = req.app_token || {};
74 + let {shareId, shareData, encryptedData, iv} = req.body;
75 + shareId = shareId || 'unknown' + crypto_utils.UUID();
76 + let share_info = {
77 + shareId, openid, appid, version, shareData
78 + }
79 +
80 + //解析用户信息密文
81 + let content
82 + try {
83 + content = await getWeAppAction(req).decrypt(appid, session_key, encryptedData, iv)
84 + } catch (e) {
85 + return Result.Error('用户信息错误')
86 + }
87 +
88 + //获取群组id
89 + share_info.openGId = content.openGId;
90 +
91 + //存储分享内容
92 + let ticket_store = new WxTokenStore(shareId + '@' + share_info.openGId, {
93 + redis,
94 + prefix: weapp_cfg.share_prefix,
95 + ttl: weapp_cfg.share_ttl
96 + });
97 + return ticket_store.saveToken(share_info)
98 + .then(function (result) {
99 + return Result.Ok('ticket', {shareId})
100 + })
101 + }
102 +
103 + /**
104 + * 步数统计
105 + * @param req
106 + * @return {Promise<*>}
107 + */
108 + this.weRunDataPost = async function (req) {
109 + let {appid, session_key, openid, unionid, version} = req.app_token || {};
110 + let {shareId, shareData, encryptedData, iv} = req.body;
111 +
112 + //解析用户信息密文
113 + let content
114 + try {
115 + content = await getWeAppAction(req).decrypt(appid, session_key, encryptedData, iv)
116 + } catch (e) {
117 + return Result.Error('用户信息错误')
118 + }
119 +
120 + const [WxStep] = po.import(dbo, ['donate_wxstep']);
121 +
122 + let stepList = [];
123 + if (content && content.stepInfoList) {
124 + stepList = content.stepInfoList.map(info => {
125 + return {
126 + id: crypto_utils.MD5(openid + info.timestamp),
127 + openid, unionid,
128 + run_date: info.timestamp,
129 + step: info.step,
130 + appid
131 + }
132 + })
133 + }
134 +
135 + // 存储数据库
136 + stepList.forEach(step => {
137 + WxStep.saveById(step)
138 + });
139 +
140 + //存储分享内容
141 + let rundata_store = new WxTokenStore(openid, {
142 + redis,
143 + prefix: weapp_cfg.werun_prefix,
144 + ttl: weapp_cfg.werun_ttl
145 + });
146 + return rundata_store.saveToken(content)
147 + .then(function (result) {
148 + return Result.Ok('success', content)
149 + })
150 + }
151 +
152 + /**
153 + * 解析分享信息
154 + * @param req
155 + * @param res
156 + * @return {Result}
157 + */
158 + this.getShareInfoPost = async function (req, res) {
159 + let {token, appid, session_key, openid, version, nickName, avatarUrl, gender, city, province, country, language} = req.app_token || {}, {shareId, encryptedData, iv} = req.body;
160 +
161 + if (!session_key) {
162 + res.status(401);
163 + return Result.Error('token not found');
164 + }
165 + // console.log('share info ===>', {token, encryptedData, iv, shareId, appid, session_key})
166 +
167 + //解析用户信息密文
168 + let content
169 + try {
170 + content = await getWeAppAction(req).decrypt(appid, session_key, encryptedData, iv)
171 + } catch (e) {
172 + return Result.Error('用户信息错误')
173 + }
174 +
175 + let openGId = content.openGId;
176 +
177 + if (!shareId) {
178 + return Result.Ok('shareInfo', {openGId})
179 + }
180 +
181 + //获取分享内容
182 + let ticket_store = new WxTokenStore(shareId + '@' + openGId, {
183 + redis,
184 + prefix: weapp_cfg.share_prefix,
185 + ttl: weapp_cfg.share_ttl
186 + });
187 + return ticket_store.getToken()
188 + .then(function (shared) {
189 + shared = shared || {};
190 + //获取围观者
191 + let lookers = shared.lookers;
192 + try {
193 + lookers = JSON.parse(lookers)
194 + } catch (e) {
195 + lookers = {}
196 + }
197 + //记录当前围观者
198 + lookers[openid] = {openid, nickName, avatarUrl, gender, city, province, country, language, version};
199 + shared.lookers = lookers;
200 + //保存分享内容
201 + return ticket_store.saveToken(shared)
202 + .then(function (shared) {
203 + logger.info('围观分享:', openid, '@', openGId)
204 + let {lookers} = shared || {}
205 + if (typeof lookers === 'string') {
206 + lookers = JSON.parse(lookers)
207 + }
208 + return Result.Ok('shareInfo', {lookers, openGId});
209 + })
210 + })
211 + }
212 +
213 + /**
214 + * 支付准备
215 + * @param req
216 + * @return {*}
217 + */
218 + this.prepayPost = function (req) {
219 + let {appid, session_key, openid, version} = req.app_token || {};
220 + if (!openid) {
221 + //没有openid需要先去授权
222 + return new Result(Result.ERROR, '请先登录');
223 + }
224 +
225 + const wxapi = getWXApi(req);
226 + const order_id = crypto_utils.UUID(), pay_receipt_id = crypto_utils.UUID(), pay_bill_id = crypto_utils.UUID(),
227 + product_id = '1234';
228 + let order_to_pay = {
229 + device_info: 'WeApp', //自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB"
230 + body: `赞赏`, //商品简单描述
231 + attach: JSON.stringify({order_id: order_id, id: pay_bill_id}), //附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用
232 + out_trade_no: pay_receipt_id, //商户系统内部订单号,要求32个字符内、且在同一个商户号下唯一;重新发起一笔支付要使用原订单号,避免重复支付
233 + total_fee: 1, //订单总金额,单位为分
234 + spbill_create_ip: req.clientIp, //APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP
235 + product_id: product_id, //trade_type=NATIVE时(即扫码支付),此参数必传。此参数为二维码中包含的商品ID,商户自行定义
236 + openid: openid, //trade_type=JSAPI时(即公众号支付),此参数必传
237 + trade_type: 'JSAPI' //取值如下:JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付
238 + };
239 +
240 + return wxapi.getBrandWCPayRequestParams(order_to_pay)
241 + .then(function (result) {
242 + if (result.ret === 'OK') {
243 + let content = result.content;
244 + content.order_id = order_id;
245 + content.pay_bill_id = pay_bill_id;
246 + }
247 + return result;
248 + });
249 + };
250 +}
1 +"use strict";
2 +const start_time = Date.now();
3 +const config = require('./init/config'),
4 + MpApiAction = require('./action/mpapi-action'),
5 + WeApiAction = require('./action/weapi-action'),
6 + BzApiAction = require('./action/bzapi-action');
7 +
8 +// 定义参数
9 +const BIND_PORT = config.system.bind_port;
10 +
11 +// 定义log4js 包含业务日志和系统日志
12 +const logger = require('./init/log4js-init').system;
13 +
14 +// 定义db
15 +logger.info('init db');
16 +require('./init/sequelize-init');
17 +
18 +
19 +// 定义express初始化
20 +logger.info('init express');
21 +const app = new (require('./init/express-init'))();
22 +
23 +
24 +//加载二级目录使用actions下的模块处理
25 +const routers_path = require('kml-express-stage-lib').routers_path;
26 +let list = routers_path.list();
27 +
28 +list.forEach(function (router_path) {
29 + let pattern = `/${router_path}`;
30 + let api_action;
31 + if (/[tc]/.test(router_path)) {
32 + api_action = new MpApiAction(router_path)
33 + } else if(/w/.test(router_path)) {
34 + api_action = new WeApiAction((router_path))
35 + } else {
36 + api_action = new BzApiAction(router_path)
37 + }
38 + app.use(pattern, api_action);
39 +});
40 +
41 +//启动服务
42 +const server = app.listen(BIND_PORT, function () {
43 + let os = require('os');
44 + let ifaces = os.networkInterfaces();
45 + let localhost = ['localhost'];
46 + Object.keys(ifaces).forEach(function (ifname) {
47 + let alias = 0;
48 +
49 + ifaces[ifname].forEach(function (iface) {
50 + if ('IPv4' !== iface.family || iface.internal !== false) {
51 + // skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses
52 + return;
53 + }
54 +
55 + if (alias >= 1) {
56 + // this single interface has multiple ipv4 addresses
57 + localhost.push(iface.address);
58 + } else {
59 + // this interface has only one ipv4 adress
60 + localhost.push(iface.address);
61 + }
62 + ++alias;
63 + });
64 + });
65 +
66 + let server_address = server.address();
67 + let port = server_address.port;
68 + logger.info('Server is listening at: ', localhost.map(function (ip) {
69 + return `http://${ip}:${port}`;
70 + }).join('\n'));
71 + logger.info((ms => `Server startup in ${ms} ms`)(Date.now() - start_time));
72 +});
73 +
74 +//bind exception event to log
75 +process.on('uncaughtException', function (e) {
76 + logger.error('uncaughtException from process', e);
77 + if (e.code === 'EADDRINUSE') {
78 + logger.error(`服务端口${BIND_PORT}被占用!`);
79 + process.exit(BIND_PORT);
80 + }
81 +});
82 +
83 +process.on('unhandledRejection', (e) => {
84 + logger.warn('unhandledRejection from process', e);
85 +});
86 +
87 +process.on('rejectionHandled', (e) => {
88 + logger.warn('rejectionHandled from process', e);
89 +});
90 +
91 +// 准备定时器
92 +if (!config.unique_schedule) {
93 + require('./crontab');
94 +}
1 +/**
2 + * data_import
3 + * Created by lintry on 2020/1/28.
4 + */
5 +const _ = require('lodash'),
6 + po = global.po,
7 + logger = global.loggers.system;
8 +const id_worker = require('../modules/id_worker');
9 +const api_sdk = new (require('kml-api-request'))();
10 +
11 +class DataImporter {
12 + constructor (dbo) {
13 + this.dbo = dbo;
14 + }
15 +
16 + /**
17 + * 导入json数据
18 + * @param json_data
19 + * @returns {Promise<void>}
20 + */
21 + async import_data (json_data) {
22 + const dbo = this.dbo;
23 + let Timeline = po.import(dbo, 'timeline_area');
24 +
25 + let areas = [];
26 +
27 + for (let row of json_data) {
28 + let {provinceName,
29 + provinceShortName,
30 + confirmedCount,
31 + suspectedCount,
32 + curedCount,
33 + deadCount,
34 + comment,
35 + cities,
36 + updateTime,
37 + country} = row;
38 +
39 + areas.push({
40 + id: id_worker.nextId(),
41 + province_name: provinceName,
42 + province_short_name: provinceShortName,
43 + confirmed_count: confirmedCount,
44 + suspected_count: suspectedCount,
45 + cured_count: curedCount,
46 + dead_count: deadCount,
47 + comment,
48 + cities,
49 + update_time: updateTime,
50 + country
51 + })
52 + }
53 +
54 + await Timeline.bulkCreate(areas, {ignoreDuplicates: true});
55 + }
56 +
57 + async import_url (url, param) {
58 + let json_data = await api_sdk.get(url, param);
59 + await this.import_data(json_data.results||[]);
60 + }
61 +}
62 +
63 +module.exports = DataImporter;