eggjs

eggjs

eggjs 上传文件

Nodejslopo1983 发表了文章 • 0 个评论 • 206 次浏览 • 2019-08-30 17:52 • 来自相关话题

'use strict';
const Controller = require('egg').Controller;
// 文件存储
const fs = require('fs');
const path = require('path');
const awaitWriteStream = require('await-stream-ready').write;
const sendToWormhole = require('stream-wormhole');

class UploadController extends Controller {
/**
*
* @param {*} stream 传入的Buffer流
* @param {*} paths 保存的路径
* @param {*} multiple 是否多文件
* @param {*} files 多文件返回
*/
async upload(stream, paths = "app/public/img", multiple = false, files = []) {
const filename = Math.random().toString(36).substr(2) + new Date().getTime() + path.extname(stream.filename).toLocaleLowerCase();
const target = path.join(this.config.baseDir, paths, filename);
const writeStream = fs.createWriteStream(target);
try {
await awaitWriteStream(stream.pipe(writeStream));
return !!multiple ? files.push(filename) : filename;
} catch (err) {
await sendToWormhole(stream);
return { code: 422, message: '上传失败,请重试!' }
}
};
// 单文件
async create() {
const ctx = this.ctx;
// 获取流
const stream = await ctx.getFileStream();
// 生成文件名
const filename = Math.random().toString(36).substr(2) + new Date().getTime() + path.extname(stream.filename).toLocaleLowerCase();
// pipe流写入信息
const target = path.join(this.config.baseDir, 'app/public/img', filename);
const writeStream = fs.createWriteStream(target);
try {
// 保存
await awaitWriteStream(stream.pipe(writeStream));
} catch (err) {
// 保存失败销毁stream 不然接口会pending到超时
await sendToWormhole(stream);
this.ctx.body = { code: 422, message: '上传失败,请重试!' }
throw err;
}
this.ctx.body = {
data: filename
};
};
// 多文件
async creates() {
const ctx = this.ctx;
// 获取文件流组
const streams = ctx.multipart();
let stream;
// 保存返回的文件信息
let files = [];
// 其他form 参数
let fields = {}
while ((stream = await streams()) != null) {
// 检查是否有其他参数 如果有写入 这里做案例 不做处理
if (stream.length) {
fields[stream[0]] = stream[1]
} else {
// 空文件处理
if (!stream.filename) {
return;
}
// 设置文件名称
const filename = Math.random().toString(36).substr(2) + new Date().getTime() + path.extname(stream.filename).toLocaleLowerCase();
// pipe 设置
const target = path.join(this.config.baseDir, 'app/public/img', filename);
const writeStream = fs.createWriteStream(target);
try {
// 保存
await awaitWriteStream(stream.pipe(writeStream));
// 写入数组
files.push({ filename, path: `/img/${filename}` })
} catch (err) {
await sendToWormhole(stream);
this.ctx.body = { code: 422, message: '上传失败,请重试!' }
throw err;
}
}
}
this.ctx.body = {
data: { files, fields }
};
}
}

module.exports = UploadController 查看全部
'use strict';
const Controller = require('egg').Controller;
// 文件存储
const fs = require('fs');
const path = require('path');
const awaitWriteStream = require('await-stream-ready').write;
const sendToWormhole = require('stream-wormhole');

class UploadController extends Controller {
/**
*
* @param {*} stream 传入的Buffer流
* @param {*} paths 保存的路径
* @param {*} multiple 是否多文件
* @param {*} files 多文件返回
*/
async upload(stream, paths = "app/public/img", multiple = false, files = []) {
const filename = Math.random().toString(36).substr(2) + new Date().getTime() + path.extname(stream.filename).toLocaleLowerCase();
const target = path.join(this.config.baseDir, paths, filename);
const writeStream = fs.createWriteStream(target);
try {
await awaitWriteStream(stream.pipe(writeStream));
return !!multiple ? files.push(filename) : filename;
} catch (err) {
await sendToWormhole(stream);
return { code: 422, message: '上传失败,请重试!' }
}
};
// 单文件
async create() {
const ctx = this.ctx;
// 获取流
const stream = await ctx.getFileStream();
// 生成文件名
const filename = Math.random().toString(36).substr(2) + new Date().getTime() + path.extname(stream.filename).toLocaleLowerCase();
// pipe流写入信息
const target = path.join(this.config.baseDir, 'app/public/img', filename);
const writeStream = fs.createWriteStream(target);
try {
// 保存
await awaitWriteStream(stream.pipe(writeStream));
} catch (err) {
// 保存失败销毁stream 不然接口会pending到超时
await sendToWormhole(stream);
this.ctx.body = { code: 422, message: '上传失败,请重试!' }
throw err;
}
this.ctx.body = {
data: filename
};
};
// 多文件
async creates() {
const ctx = this.ctx;
// 获取文件流组
const streams = ctx.multipart();
let stream;
// 保存返回的文件信息
let files = [];
// 其他form 参数
let fields = {}
while ((stream = await streams()) != null) {
// 检查是否有其他参数 如果有写入 这里做案例 不做处理
if (stream.length) {
fields[stream[0]] = stream[1]
} else {
// 空文件处理
if (!stream.filename) {
return;
}
// 设置文件名称
const filename = Math.random().toString(36).substr(2) + new Date().getTime() + path.extname(stream.filename).toLocaleLowerCase();
// pipe 设置
const target = path.join(this.config.baseDir, 'app/public/img', filename);
const writeStream = fs.createWriteStream(target);
try {
// 保存
await awaitWriteStream(stream.pipe(writeStream));
// 写入数组
files.push({ filename, path: `/img/${filename}` })
} catch (err) {
await sendToWormhole(stream);
this.ctx.body = { code: 422, message: '上传失败,请重试!' }
throw err;
}
}
}
this.ctx.body = {
data: { files, fields }
};
}
}

module.exports = UploadController

Node+eggjs+mongodb 一步步实现 CRM(2)需求整理

每天进步一点点lopo1983 发表了文章 • 0 个评论 • 379 次浏览 • 2019-03-04 10:00 • 来自相关话题

?























部分附件收限制无法上传(懒得折腾) 请解压缩查看 查看全部
功能结构.png

?
设置.png


资金.png


客户.png


辅助资料.png


部门与员工.png

部分附件收限制无法上传(懒得折腾) 请解压缩查看

Node+eggjs+mongodb 一步步实现 CRM(1)环境搭建

每天进步一点点lopo1983 发表了文章 • 0 个评论 • 502 次浏览 • 2019-02-28 11:24 • 来自相关话题

本文章建议有一定Nodejs开发经验和熟悉ES6/7的开发人员查看,文中若有错误望指出!
?1.安装nodejs?
?
? ?建议选择?LTS 版本,最低要求 8.x(机器配置不好的,请考虑8.x)。
?
2.egg 安装$ npm i egg-init -g
$ egg-init egg-crm --type=simple
$ cd egg-crm
$ npm inpm run devegg文档地址
?
3.mongoDB安装 (4.x)
?
建议在服务器上安装,以便随时可以开发,这里以centos?安装为例
步骤如下:
创建该路径文件 /etc/yum.repos.d/mongodb-org-4.0.repo[mongodb-org-4.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-4.0.asc安装sudo yum install -y mongodb-org-4.0.6 mongodb-org-server-4.0.6 mongodb-org-shell-4.0.6 mongodb-org-mongos-4.0.6 mongodb-org-tools-4.0.6启动service mongod start?具体可参考官方文档
?
4.相关插件配置 "devDependencies": {
"autod": "^3.0.1",
"autod-egg": "^1.1.0",
"baidu-aip-sdk": "^2.3.9",
"bce-sdk-js": "^0.2.9",
"decimal": "^0.0.2",
"egg-bin": "^4.11.0",
"egg-ci": "^1.11.0",
"egg-cors": "^2.1.2",
"egg-jwt": "^3.1.6",
"egg-mock": "^3.21.0",
"egg-mongoose": "^3.1.1",
"egg-validate": "^2.0.2",
"eslint": "^5.13.0",
"eslint-config-egg": "^7.1.0",
"lodash": "^4.17.11",
"stream-to-array": "^2.3.0",
"webstorm-disable-index": "^1.2.0",
"xml2js": "^0.4.19"
},
baidu-aip-sdk:百度AI接口用于智能审核 OCR等
bce-sdk-js: 百度BCE 接口 我们会用到百度的BOS 云存储
decimal:处理JS浮点误差
egg-cors:egg跨域
egg-jwt:egg jsonWebToken
egg-mongoose:mongo数据库链接
egg-validate:egg数据校验
lodash:一个十分优秀的函数编程库
stream-to-array:流处理
xml2js:微信支付

4.egg相关配置
?
config.default.js(相关留空的数据请填入自己的数据)'use strict';

module.exports = appInfo => {
const config = exports = {};

// use for cookie sign key, should change to your own and keep security
config.keys = appInfo.name + '_1539582326426_4353';

// csrf配置
config.security = {
csrf: {
enable: false,
ignoreJSON: true
},
domainWhiteList: ['http://localhost:7001', 'http://192.168.0.123', 'http://localhost]
};
//
config.cors = {
allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS',
credentials: true
};
// // add your config here
// config.middleware = ;
config.middleware = ['errorHandler', 'responseFormatter', 'jwtErrorHandler'];
config.middleware.errorHandler = {
match: '/api',
};
config.middleware.responseFormatter = {
match: '/api',
};
//短信配置
config.sms = {
svip: {
uri: ‘',
name: '',
password: '',
},
};
// 邮箱配置
config.email = {
service: 'QQex',
port: 465,
secureConnection: true,
auth: {
user: '',
pass: '',
}
}
// mongodb配置
config.mongoose = {
url: 'mongodb://127.0.0.1/geecrm',
options: {},
};
// bos云存储
/*****************begin********************/
config.baiduBos = {
endpoint: 'http://bj.bcebos.com',
credentials: {
ak: '',
sk: ''
}
};
config.baiduBosBucket = '';
/*****************end***********************/
// baidu AIP
config.baiduAIP = {
APP_ID: '',
API_KEY: '',
SECRET_KEY: ''
};
// baidu VOD
config.baiduVod = {
endpoint: 'http://vod.bj.baidubce.com',
credentials: {
ak: '',
sk: ''
}
};
// 企业微信配置
config.weWork = {
'corpid': '',
'corpsecret': '',
'agentId': ''
};
// 微信配置
config.wechatApi = {
appId: '',
appSecret: '',
};
// 默认json 设置
config.JSONS = {
'code': 200,
'message': 'success',
'uri': 'https://api.lanjing.xyz',
};
config.alipay = {
appId: "",
rsaPrivate: "",
notifyUrl: "", //异步回调
signType: "RSA2",
rsaPublic: "",
sandbox: false //沙箱环境
}

config.wechatPay = {
partnerKey: "",
appId: "",
mchId: "",
notifyUrl: "http://www.langjing.xyz/wechat/notify", //异步回调,到微信平台设置下
pfx: ""
}
//
return config;
};
??plugin.js ? ? ? ? ?'use strict';
exports.validate = {
enable: true,
package: 'egg-validate',
};
exports.mongoose = {
enable: true,
package: 'egg-mongoose',
};
exports.jwt = {
enable: true,
package: "egg-jwt"
};
exports.cors = {
enable: true,
package: "egg-cors"
};

?
?项目目录结构 (文件夹可按需建立无内容留空即可)

?





? 查看全部

本文章建议有一定Nodejs开发经验和熟悉ES6/7的开发人员查看,文中若有错误望指出!


?1.安装nodejs?
?
? ?建议选择?LTS 版本,最低要求 8.x(机器配置不好的,请考虑8.x)。
?
2.egg 安装
$ npm i egg-init -g
$ egg-init egg-crm --type=simple
$ cd egg-crm
$ npm i
npm run dev
egg文档地址
?
3.mongoDB安装 (4.x)
?
建议在服务器上安装,以便随时可以开发,这里以centos?安装为例
步骤如下:
创建该路径文件 /etc/yum.repos.d/mongodb-org-4.0.repo
[mongodb-org-4.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-4.0.asc
安装
sudo yum install -y mongodb-org-4.0.6 mongodb-org-server-4.0.6 mongodb-org-shell-4.0.6 mongodb-org-mongos-4.0.6 mongodb-org-tools-4.0.6
启动
service mongod start
?具体可参考官方文档
?
4.相关插件配置
  "devDependencies": {
"autod": "^3.0.1",
"autod-egg": "^1.1.0",
"baidu-aip-sdk": "^2.3.9",
"bce-sdk-js": "^0.2.9",
"decimal": "^0.0.2",
"egg-bin": "^4.11.0",
"egg-ci": "^1.11.0",
"egg-cors": "^2.1.2",
"egg-jwt": "^3.1.6",
"egg-mock": "^3.21.0",
"egg-mongoose": "^3.1.1",
"egg-validate": "^2.0.2",
"eslint": "^5.13.0",
"eslint-config-egg": "^7.1.0",
"lodash": "^4.17.11",
"stream-to-array": "^2.3.0",
"webstorm-disable-index": "^1.2.0",
"xml2js": "^0.4.19"
},

baidu-aip-sdk:百度AI接口用于智能审核 OCR等
bce-sdk-js: 百度BCE 接口 我们会用到百度的BOS 云存储
decimal:处理JS浮点误差
egg-cors:egg跨域
egg-jwt:egg jsonWebToken
egg-mongoose:mongo数据库链接
egg-validate:egg数据校验
lodash:一个十分优秀的函数编程库
stream-to-array:流处理
xml2js:微信支付

4.egg相关配置
?
config.default.js(相关留空的数据请填入自己的数据)
'use strict';

module.exports = appInfo => {
const config = exports = {};

// use for cookie sign key, should change to your own and keep security
config.keys = appInfo.name + '_1539582326426_4353';

// csrf配置
config.security = {
csrf: {
enable: false,
ignoreJSON: true
},
domainWhiteList: ['http://localhost:7001', 'http://192.168.0.123', 'http://localhost]
};
//
config.cors = {
allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS',
credentials: true
};
// // add your config here
// config.middleware = ;
config.middleware = ['errorHandler', 'responseFormatter', 'jwtErrorHandler'];
config.middleware.errorHandler = {
match: '/api',
};
config.middleware.responseFormatter = {
match: '/api',
};
//短信配置
config.sms = {
svip: {
uri: ‘',
name: '',
password: '',
},
};
// 邮箱配置
config.email = {
service: 'QQex',
port: 465,
secureConnection: true,
auth: {
user: '',
pass: '',
}
}
// mongodb配置
config.mongoose = {
url: 'mongodb://127.0.0.1/geecrm',
options: {},
};
// bos云存储
/*****************begin********************/
config.baiduBos = {
endpoint: 'http://bj.bcebos.com',
credentials: {
ak: '',
sk: ''
}
};
config.baiduBosBucket = '';
/*****************end***********************/
// baidu AIP
config.baiduAIP = {
APP_ID: '',
API_KEY: '',
SECRET_KEY: ''
};
// baidu VOD
config.baiduVod = {
endpoint: 'http://vod.bj.baidubce.com',
credentials: {
ak: '',
sk: ''
}
};
// 企业微信配置
config.weWork = {
'corpid': '',
'corpsecret': '',
'agentId': ''
};
// 微信配置
config.wechatApi = {
appId: '',
appSecret: '',
};
// 默认json 设置
config.JSONS = {
'code': 200,
'message': 'success',
'uri': 'https://api.lanjing.xyz',
};
config.alipay = {
appId: "",
rsaPrivate: "",
notifyUrl: "", //异步回调
signType: "RSA2",
rsaPublic: "",
sandbox: false //沙箱环境
}

config.wechatPay = {
partnerKey: "",
appId: "",
mchId: "",
notifyUrl: "http://www.langjing.xyz/wechat/notify", //异步回调,到微信平台设置下
pfx: ""
}
//
return config;
};

??plugin.js ? ? ? ? ?
'use strict';
exports.validate = {
enable: true,
package: 'egg-validate',
};
exports.mongoose = {
enable: true,
package: 'egg-mongoose',
};
exports.jwt = {
enable: true,
package: "egg-jwt"
};
exports.cors = {
enable: true,
package: "egg-cors"
};


?
?项目目录结构 (文件夹可按需建立无内容留空即可)

?
QQ图片20190304101503.png


?

Nodejs 常用helper函数(持续更新)

Nodejslopo1983 发表了文章 • 0 个评论 • 314 次浏览 • 2018-12-29 13:38 • 来自相关话题

const crypto = require('crypto');
const toArray = require('stream-to-array');
const sendToWormhole = require('stream-wormhole');
const moment = require('moment');
const Decimal = require('decimal');
const _Array = require('lodash/array')
const _Collection = require('lodash/collection')
Date.prototype.format = function () {
let s = '';
s += this.getFullYear() + '-';
s += (this.getMonth() + 1) + "-";
s += this.getDate();
return (s);
};
module.exports = {
/**
* GET querys 分离函数
* @param {String} pages 分页 [上一页最后一个数据的_id,分页数量] 分页数量默认为10
* @param {String} fields 需要展示的字段 a,b,c,d
* @param {String} unFields 不需要展示的字段 a,b,c,d
* @param {String} querys 查询条件 如 a=1&b=2&c=3
* @param {String} orderBy 排序字段
* @param {String} sort 排序方式【1:正序,-1:倒序】(必须与orderBy同时出现)
* @param {String} dates 时间范围/时间查询 [开始时间,结束时间]/查询时间
*
* @version 1.0.1
* 2018/10/17 修改返回值类型可直接供mongoose使用
*
* @example
* GET /user/?orderBy=compName&sort=-1&pages=20,1&fields=compName,compAuth,compEmail&unFields=status&dates=2018-8-5&name=lopo
* index(){
* const {ctx} = this;
* const {queryParamFn} = ctx.helper;
* ctx.body = queryParamFn(ctx.query);
* }
*
* @return {Object}
* {
* querys: { name: 'lopo' },
* select: { compName: 1, compAuth: 1, compEmail: 1, status: 0 },
* pages: { marker: '1', limit: '20' },
* sort: { compName: '-1' },
* dates: '2018-8-5'
* }
*/
queryParamFn(querys, db = false) {
const mapFN = (str) => (field, val) => str[field].split(',').reduce((a, b) => {
a[b] = val;
return a
}, {});
let strToArr = mapFN(querys);
const sort = !!querys.sort ? JSON.parse(querys.sort) : {};
// const dates = querys.dates || [];
const fields = !!querys.fields ? strToArr('fields', 1) : {};
const unFields = !!querys.unFields ? strToArr('unFields', 0) : {};
const limit = !!querys.limit ? querys.limit * 1 : 10;
const marker = querys.marker || ''
const dateFn = () => {
if (!!dates) {
const ARR = dates.split(',');
return ARR.length === 2 ? {
'$gte': ARR[1],
'$lte': ARR[0]
} : `${ARR}`
}
}
delete querys.pages;
delete querys.fields;
delete querys.unFields;
delete querys.sort;
delete querys.dates;
delete querys.marker;
delete querys.limit;
delete querys._;
delete querys._type;
return {
'querys': {
...querys,
} || {},
'select': {
...fields,
...unFields
},
limit,
marker,
sort,
// ...!!dates && {
// 'dates': dateFn()
// }
}
},
/**
* PD分离
* @param {String} psd PD
* @param {String} pilipala chicken
*
* @return {String} 返回分离后的字符串
*/
bilibole(psd, pilipala) {
let bilibole = crypto.createHash('md5').update(`${psd}:${pilipala}`).digest('hex');
return `${pilipala}${bilibole}`;
},
/**
* @name stream 转buffer
*
* @param {files} stream 文件流
* @param {Function} fn 文件处理函数
*
* @return {String:Buffer} 返回Buferr 可用函数对Buffer进行处理后返回
*/
async stream2buf(stream, fn) {
try {
const parts = await toArray(stream);
const buf = Buffer.concat(parts);
return !!fn ? fn(buf) : buf
} catch (err) {
await sendToWormhole(stream);
throw err;
};
},
/**
* len 随机位数 默认6位
* @param {Number} len 随机数字长度 默认为6
*/
roundCode: (len = 6) => {
let pwd = "";
for (let idx = 0; idx < len; idx++) {
let seed = parseInt(Math.random() * 9);
pwd += seed;
}
return pwd;
},
/**
* 获取token函数
* @param {String} type token类型 ['BAIDUAip','WEWork','WeiXin','DirectAdmin','openstack']
*/
async TokenFn(type) {
const {
ctx,
config
} = this;
let datas;
const DirectAdmin = config.vhost.DirectAdmin;
const wechatApi = config.wechatApi;
const openstackApi = config.openstack;
const tokenMapFn = async () => {
const maps = {
// 百度token
'BAIDUAip': await ctx.curl('aip.baidubce.com/oauth/2.0/token', {
method: 'POST',
dataType: 'json',
data: {
'grant_type': 'client_credentials',
'client_id': config.baiduAIP.API_KEY,
'client_secret': config.baiduAIP.SECRET_KEY
}
}),
// 企业微信token
'WEWork': await ctx.curl('https://qyapi.weixin.qq.com/cgi-bin/gettoken', {
dataType: 'json',
data: {
'corpid': config.weWork.corpid,
'corpsecret': config.weWork.corpsecret
}
}),
// 微信token
'WeiXin': await ctx.curl('https://api.weixin.qq.com/cgi-bin/token', {
dataType: 'json',
data: {
'grant_type': 'client_credential',
'appid': wechatApi.appId,
'secret': wechatApi.appSecret
}
}),
// // DA token
// 'DirectAdmin': await ctx.curl(`${DirectAdmin.server}:${DirectAdmin.port}/CMD_LOGIN`, {
// method: 'POST',
// data: {
// 'username': DirectAdmin.username,
// 'password': DirectAdmin.password
// }
// }),
// openstack token
'openstack': await ctx.curl(`${openstackApi.uri}:5000/v3/auth/tokens?nocatalog`, {
method: 'POST',
dataType: 'json',
headers: {
'Content-Type': 'application/json',
},
content: `{
"auth": {
"identity": {
"methods": ["password"],
"password": {
"user": {
"name": "${openstackApi.name}",
"password": "${openstackApi.password}",
"domain": { "name": "Default" }
}
}
},
"scope": {
"project": {
"domain": {
"id": "default"
},
"name": "admin"
}
}
}
}`,
})
};
return maps[type]
};
const setResult = async () => {
if (type !== 'DirectAdmin') {
const data = await tokenMapFn();
return {
tokenValue: type === 'openstack' ? data.headers['x-subject-token'] : data.data.access_token,
tokenType: type,
tokenExpiresIn: type === 'openstack' ? new Date(data.data.token.expires_at) - 0 - 60 * 1000 : data.data.expires_in * 1000 + (new Date() - 0)
}
} else {
const cookies = (await tokenMapFn()).res.headers['set-cookie'][0].split(';');
return {
tokenValue: cookies[0],
tokenType: 'DirectAdmin',
tokenExpiresIn: (new Date(cookies[2].split('=')[1]) - 0) + 8 * 60 * 60 * 1000
}
}
};
const setToken = async () => {
const saveToken = await ctx.model.Tokens.findOneAndUpdate({
'tokenType': type
}, { ...(await setResult())
}, {
upsert: true,
new: true
});
datas = saveToken.tokenValue;
};
const tokens = await ctx.model.Tokens.findOne({
'tokenType': type
});
!!tokens && tokens.tokenExpiresIn > (new Date - 0) ? datas = tokens.tokenValue : await setToken();
return {
data: datas
}
},
/**
* mongo报错处理
*/
mongoError(error) {
const errorMap = {
'11000': ''
}
},
/**
* 函数柯里化
*/
curry: (fn, arity = fn.length, ...args) => arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args),
/**
* moment mix
* @param {String} scope [/day/week/month/quarter/year] 日/周/月/季度/年
* @int {Number} int 数值
*/
// 当前时间
getThisMoment: (scope) => {
return {
startDate: moment().startOf(scope).format(),
endDate: moment().format()
}
},
// 之前
getPrevMoment: (scope) => {
return {
startDate: moment().week(moment()[scope]() - 1).startOf(scope).format(),
endDate: moment().week(moment()[scope]() - 1).endOf(scope).format()
}
},
// 时间差 type [add,subtract]
subtractMoment: (int, scope, type = "subtract", withFormat = true, fn) => {
const dates = moment()[type](int, scope);
return !fn ? (withFormat ? dates.format() : dates) : fn(dates);
},
/**
* @name 格式化时间
* @param {String} formater 格式化输出标准 YYYY/MM/DD/HH/mm/ss/SSS
* @param {Date} dates 日期
* @param {Boolean} utc UTC
*/

formatDate: ({
formater,
dates
} = {}, utc = false) => {
const DATE = moment(!!dates ? dates : new Date());
return utc ? DATE.utc().format(!!formater && formater) : DATE.format(!!formater && formater)
},
// 生成时间数组
getDayAll: (begin, end) => {
let dateAllArr = new Array();
let ab = moment(begin).format("YYYY-MM-DD").split("-");
const ae = moment(end).format("YYYY-MM-DD").split("-");
const db = new Date();
db.setUTCFullYear(ab[0], ab[1] - 1, ab[2]);
const de = new Date();
de.setUTCFullYear(ae[0], ae[1] - 1, ae[2]);
const unixDb = db.getTime();
const unixDe = de.getTime();
for (let k = unixDb; k <= unixDe;) {
dateAllArr.push(new Date(parseInt(k)).format().toString());
k = k + 24 * 60 * 60 * 1000;
}
return dateAllArr;
},
getCTX: () => app.createAnonymousContext(),
// 发送短信,邮件,企业微信通知
async sendWarning(ctx, content, _id, compName) {
const auth = ctx.state.user;
const {
compEmail,
compPhone
} = await ctx.model.Company.findOne({
'_id': _id
}, {
'compEmail': 1,
'compPhone': 1
});
// await ctx.service.wework.message.send({ totag: 2, content: content.replace(/您/g, `${compName}`) });
await ctx.service.sms.svipwang.create(compPhone, content);
await ctx.service.sms.mail.sendMailDefault({
subject: '系统消息通知',
to: compEmail,
html: content
});
},
// 清理paramsFN()
deleteUndefined: (object) => {
for (let key in object) {
key !== 'detail' && delete object[(object[key] === undefined || object[key] === '' || object[key] === '[]') && key]
}
return object
},
/******************************************************************************************/
/**
* class DistributedEdit extends mix(class1, class2)
*/
// mixins
mix: (...mixins) => {
class Mix {}

for (let mixin of mixins) {
copyProperties(Mix.prototype, mixin); // 拷贝实例属性
copyProperties(Mix.prototype, Reflect.getPrototypeOf(mixin)); // 拷贝原型属性
}

return Mix;
},
// mixinsParams
copyProperties: (target, source) => {
for (let key of Reflect.ownKeys(source)) {
if (key !== "constructor" &&
key !== "prototype" &&
key !== "name"
) {
let desc = Object.getOwnPropertyDescriptor(source, key);
Object.defineProperty(target, key, desc);
}
}
},
/***************end****************/
/**
* 货币计算 mix
* @param {String} method [add/+,sub/-,mul/*,div//] 加减乘除
* @param {Array} money 需要计算的数字【前,后】
*/
decimalCash: (method = "add", money) => {
return new Decimal(money[0])[method](new Decimal(money[1])).toNumber()
},
// decimaFN: () => { return this.decimalCash() },
// decimalAdd: () => { return this.decimaFN('add') },
// decimalSub: () => { return this.decimaFN('sub') },
// decimalMul: () => { return this.decimaFN('mul') },
// decimalDiv: () => { return this.decimaFN('div') },
/****************end***************/
/**
* MONGO type转换
* @param {Array} ARR 字符串字典
* @param {Number} value 值
* @param {Boolean} hasZero 是否从0开始
*/
NumToStr(ARR, value, hasZero = false) {
return `${value}|${ARR[hasZero ? value : value - 1]}`
},
/**
* @name mongoDB update数组参数
*
*/
objToQuery(OBJ, KEY) {
return Object.keys(OBJ).reduce((a, b) => {
a[`${KEY}.$.${b}`] = OBJ[b];
return a
}, {});
},
/**
* @name 线转树
* @param {Array} ARR 需要处理的数组
* @param {String} keyName 作为分类的字段
*/
Array2Object: (ARR, keyName) => {
return ARR.reduce((a, b) => {
const keys = b[keyName];
if (a[keys] === undefined) a[keys] = [];
a[keys].push(b);
return a;
}, {})
},
/**
* @name 字典排序
*/
OBJKeySort: (obj) => {
const newkey = Object.keys(obj).sort();
const newObj = {};
for (var i = 0; i < newkey.length; i++) {
newObj[newkey[i]] = obj[newkey[i]];
}
return newObj;
},
/**
* @name 字母升序
*
* @param {String} chars 字母
* @param {Number} num 进几位
*
*/
nextChars: (chars, num, type = 'lower') => {
const char = chars.toLowerCase();
const isChar = /^[a-zA-Z]*$/.test(char);
const cx = char.charCodeAt(0);
const CHARS = (!!isChar && cx + num < 123) ? String.fromCharCode(char.charCodeAt(0) + num) : false;
return !!CHARS ? type === 'upper' ? CHARS.toUpperCase() : CHARS : 'Params Error'
},
/**
* @name 扁平化数组 fr : _.Array
*
* @param {Array} arr 数组
*
*/
deepFlatten: arr => _Array.flatten(arr),
/**
* @name 集合排序 fr : _.Collection
*
* @param {String|Function|Array|Object} 排序字段
* @param {String} asc|desc 排序方式
*
*/
orderBy: (arr, iteratees, order = 'asc') => _Collection.orderBy(arr, iteratees, order),
/**
* @name 隐藏手机号中间4位
*
*@param {String} telphone 手机号码
*/
hidePhone: (telphone) => {
const reg = /^(\d{3})\d{4}(\d{4})$/;
return telphone.replace(reg, `$1****$2`)
}
}; 查看全部
const crypto = require('crypto');
const toArray = require('stream-to-array');
const sendToWormhole = require('stream-wormhole');
const moment = require('moment');
const Decimal = require('decimal');
const _Array = require('lodash/array')
const _Collection = require('lodash/collection')
Date.prototype.format = function () {
let s = '';
s += this.getFullYear() + '-';
s += (this.getMonth() + 1) + "-";
s += this.getDate();
return (s);
};
module.exports = {
/**
* GET querys 分离函数
* @param {String} pages 分页 [上一页最后一个数据的_id,分页数量] 分页数量默认为10
* @param {String} fields 需要展示的字段 a,b,c,d
* @param {String} unFields 不需要展示的字段 a,b,c,d
* @param {String} querys 查询条件 如 a=1&b=2&c=3
* @param {String} orderBy 排序字段
* @param {String} sort 排序方式【1:正序,-1:倒序】(必须与orderBy同时出现)
* @param {String} dates 时间范围/时间查询 [开始时间,结束时间]/查询时间
*
* @version 1.0.1
* 2018/10/17 修改返回值类型可直接供mongoose使用
*
* @example
* GET /user/?orderBy=compName&sort=-1&pages=20,1&fields=compName,compAuth,compEmail&unFields=status&dates=2018-8-5&name=lopo
* index(){
* const {ctx} = this;
* const {queryParamFn} = ctx.helper;
* ctx.body = queryParamFn(ctx.query);
* }
*
* @return {Object}
* {
* querys: { name: 'lopo' },
* select: { compName: 1, compAuth: 1, compEmail: 1, status: 0 },
* pages: { marker: '1', limit: '20' },
* sort: { compName: '-1' },
* dates: '2018-8-5'
* }
*/
queryParamFn(querys, db = false) {
const mapFN = (str) => (field, val) => str[field].split(',').reduce((a, b) => {
a[b] = val;
return a
}, {});
let strToArr = mapFN(querys);
const sort = !!querys.sort ? JSON.parse(querys.sort) : {};
// const dates = querys.dates || [];
const fields = !!querys.fields ? strToArr('fields', 1) : {};
const unFields = !!querys.unFields ? strToArr('unFields', 0) : {};
const limit = !!querys.limit ? querys.limit * 1 : 10;
const marker = querys.marker || ''
const dateFn = () => {
if (!!dates) {
const ARR = dates.split(',');
return ARR.length === 2 ? {
'$gte': ARR[1],
'$lte': ARR[0]
} : `${ARR}`
}
}
delete querys.pages;
delete querys.fields;
delete querys.unFields;
delete querys.sort;
delete querys.dates;
delete querys.marker;
delete querys.limit;
delete querys._;
delete querys._type;
return {
'querys': {
...querys,
} || {},
'select': {
...fields,
...unFields
},
limit,
marker,
sort,
// ...!!dates && {
// 'dates': dateFn()
// }
}
},
/**
* PD分离
* @param {String} psd PD
* @param {String} pilipala chicken
*
* @return {String} 返回分离后的字符串
*/
bilibole(psd, pilipala) {
let bilibole = crypto.createHash('md5').update(`${psd}:${pilipala}`).digest('hex');
return `${pilipala}${bilibole}`;
},
/**
* @name stream 转buffer
*
* @param {files} stream 文件流
* @param {Function} fn 文件处理函数
*
* @return {String:Buffer} 返回Buferr 可用函数对Buffer进行处理后返回
*/
async stream2buf(stream, fn) {
try {
const parts = await toArray(stream);
const buf = Buffer.concat(parts);
return !!fn ? fn(buf) : buf
} catch (err) {
await sendToWormhole(stream);
throw err;
};
},
/**
* len 随机位数 默认6位
* @param {Number} len 随机数字长度 默认为6
*/
roundCode: (len = 6) => {
let pwd = "";
for (let idx = 0; idx < len; idx++) {
let seed = parseInt(Math.random() * 9);
pwd += seed;
}
return pwd;
},
/**
* 获取token函数
* @param {String} type token类型 ['BAIDUAip','WEWork','WeiXin','DirectAdmin','openstack']
*/
async TokenFn(type) {
const {
ctx,
config
} = this;
let datas;
const DirectAdmin = config.vhost.DirectAdmin;
const wechatApi = config.wechatApi;
const openstackApi = config.openstack;
const tokenMapFn = async () => {
const maps = {
// 百度token
'BAIDUAip': await ctx.curl('aip.baidubce.com/oauth/2.0/token', {
method: 'POST',
dataType: 'json',
data: {
'grant_type': 'client_credentials',
'client_id': config.baiduAIP.API_KEY,
'client_secret': config.baiduAIP.SECRET_KEY
}
}),
// 企业微信token
'WEWork': await ctx.curl('https://qyapi.weixin.qq.com/cgi-bin/gettoken', {
dataType: 'json',
data: {
'corpid': config.weWork.corpid,
'corpsecret': config.weWork.corpsecret
}
}),
// 微信token
'WeiXin': await ctx.curl('https://api.weixin.qq.com/cgi-bin/token', {
dataType: 'json',
data: {
'grant_type': 'client_credential',
'appid': wechatApi.appId,
'secret': wechatApi.appSecret
}
}),
// // DA token
// 'DirectAdmin': await ctx.curl(`${DirectAdmin.server}:${DirectAdmin.port}/CMD_LOGIN`, {
// method: 'POST',
// data: {
// 'username': DirectAdmin.username,
// 'password': DirectAdmin.password
// }
// }),
// openstack token
'openstack': await ctx.curl(`${openstackApi.uri}:5000/v3/auth/tokens?nocatalog`, {
method: 'POST',
dataType: 'json',
headers: {
'Content-Type': 'application/json',
},
content: `{
"auth": {
"identity": {
"methods": ["password"],
"password": {
"user": {
"name": "${openstackApi.name}",
"password": "${openstackApi.password}",
"domain": { "name": "Default" }
}
}
},
"scope": {
"project": {
"domain": {
"id": "default"
},
"name": "admin"
}
}
}
}`,
})
};
return maps[type]
};
const setResult = async () => {
if (type !== 'DirectAdmin') {
const data = await tokenMapFn();
return {
tokenValue: type === 'openstack' ? data.headers['x-subject-token'] : data.data.access_token,
tokenType: type,
tokenExpiresIn: type === 'openstack' ? new Date(data.data.token.expires_at) - 0 - 60 * 1000 : data.data.expires_in * 1000 + (new Date() - 0)
}
} else {
const cookies = (await tokenMapFn()).res.headers['set-cookie'][0].split(';');
return {
tokenValue: cookies[0],
tokenType: 'DirectAdmin',
tokenExpiresIn: (new Date(cookies[2].split('=')[1]) - 0) + 8 * 60 * 60 * 1000
}
}
};
const setToken = async () => {
const saveToken = await ctx.model.Tokens.findOneAndUpdate({
'tokenType': type
}, { ...(await setResult())
}, {
upsert: true,
new: true
});
datas = saveToken.tokenValue;
};
const tokens = await ctx.model.Tokens.findOne({
'tokenType': type
});
!!tokens && tokens.tokenExpiresIn > (new Date - 0) ? datas = tokens.tokenValue : await setToken();
return {
data: datas
}
},
/**
* mongo报错处理
*/
mongoError(error) {
const errorMap = {
'11000': ''
}
},
/**
* 函数柯里化
*/
curry: (fn, arity = fn.length, ...args) => arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args),
/**
* moment mix
* @param {String} scope [/day/week/month/quarter/year] 日/周/月/季度/年
* @int {Number} int 数值
*/
// 当前时间
getThisMoment: (scope) => {
return {
startDate: moment().startOf(scope).format(),
endDate: moment().format()
}
},
// 之前
getPrevMoment: (scope) => {
return {
startDate: moment().week(moment()[scope]() - 1).startOf(scope).format(),
endDate: moment().week(moment()[scope]() - 1).endOf(scope).format()
}
},
// 时间差 type [add,subtract]
subtractMoment: (int, scope, type = "subtract", withFormat = true, fn) => {
const dates = moment()[type](int, scope);
return !fn ? (withFormat ? dates.format() : dates) : fn(dates);
},
/**
* @name 格式化时间
* @param {String} formater 格式化输出标准 YYYY/MM/DD/HH/mm/ss/SSS
* @param {Date} dates 日期
* @param {Boolean} utc UTC
*/

formatDate: ({
formater,
dates
} = {}, utc = false) => {
const DATE = moment(!!dates ? dates : new Date());
return utc ? DATE.utc().format(!!formater && formater) : DATE.format(!!formater && formater)
},
// 生成时间数组
getDayAll: (begin, end) => {
let dateAllArr = new Array();
let ab = moment(begin).format("YYYY-MM-DD").split("-");
const ae = moment(end).format("YYYY-MM-DD").split("-");
const db = new Date();
db.setUTCFullYear(ab[0], ab[1] - 1, ab[2]);
const de = new Date();
de.setUTCFullYear(ae[0], ae[1] - 1, ae[2]);
const unixDb = db.getTime();
const unixDe = de.getTime();
for (let k = unixDb; k <= unixDe;) {
dateAllArr.push(new Date(parseInt(k)).format().toString());
k = k + 24 * 60 * 60 * 1000;
}
return dateAllArr;
},
getCTX: () => app.createAnonymousContext(),
// 发送短信,邮件,企业微信通知
async sendWarning(ctx, content, _id, compName) {
const auth = ctx.state.user;
const {
compEmail,
compPhone
} = await ctx.model.Company.findOne({
'_id': _id
}, {
'compEmail': 1,
'compPhone': 1
});
// await ctx.service.wework.message.send({ totag: 2, content: content.replace(/您/g, `${compName}`) });
await ctx.service.sms.svipwang.create(compPhone, content);
await ctx.service.sms.mail.sendMailDefault({
subject: '系统消息通知',
to: compEmail,
html: content
});
},
// 清理paramsFN()
deleteUndefined: (object) => {
for (let key in object) {
key !== 'detail' && delete object[(object[key] === undefined || object[key] === '' || object[key] === '[]') && key]
}
return object
},
/******************************************************************************************/
/**
* class DistributedEdit extends mix(class1, class2)
*/
// mixins
mix: (...mixins) => {
class Mix {}

for (let mixin of mixins) {
copyProperties(Mix.prototype, mixin); // 拷贝实例属性
copyProperties(Mix.prototype, Reflect.getPrototypeOf(mixin)); // 拷贝原型属性
}

return Mix;
},
// mixinsParams
copyProperties: (target, source) => {
for (let key of Reflect.ownKeys(source)) {
if (key !== "constructor" &&
key !== "prototype" &&
key !== "name"
) {
let desc = Object.getOwnPropertyDescriptor(source, key);
Object.defineProperty(target, key, desc);
}
}
},
/***************end****************/
/**
* 货币计算 mix
* @param {String} method [add/+,sub/-,mul/*,div//] 加减乘除
* @param {Array} money 需要计算的数字【前,后】
*/
decimalCash: (method = "add", money) => {
return new Decimal(money[0])[method](new Decimal(money[1])).toNumber()
},
// decimaFN: () => { return this.decimalCash() },
// decimalAdd: () => { return this.decimaFN('add') },
// decimalSub: () => { return this.decimaFN('sub') },
// decimalMul: () => { return this.decimaFN('mul') },
// decimalDiv: () => { return this.decimaFN('div') },
/****************end***************/
/**
* MONGO type转换
* @param {Array} ARR 字符串字典
* @param {Number} value 值
* @param {Boolean} hasZero 是否从0开始
*/
NumToStr(ARR, value, hasZero = false) {
return `${value}|${ARR[hasZero ? value : value - 1]}`
},
/**
* @name mongoDB update数组参数
*
*/
objToQuery(OBJ, KEY) {
return Object.keys(OBJ).reduce((a, b) => {
a[`${KEY}.$.${b}`] = OBJ[b];
return a
}, {});
},
/**
* @name 线转树
* @param {Array} ARR 需要处理的数组
* @param {String} keyName 作为分类的字段
*/
Array2Object: (ARR, keyName) => {
return ARR.reduce((a, b) => {
const keys = b[keyName];
if (a[keys] === undefined) a[keys] = [];
a[keys].push(b);
return a;
}, {})
},
/**
* @name 字典排序
*/
OBJKeySort: (obj) => {
const newkey = Object.keys(obj).sort();
const newObj = {};
for (var i = 0; i < newkey.length; i++) {
newObj[newkey[i]] = obj[newkey[i]];
}
return newObj;
},
/**
* @name 字母升序
*
* @param {String} chars 字母
* @param {Number} num 进几位
*
*/
nextChars: (chars, num, type = 'lower') => {
const char = chars.toLowerCase();
const isChar = /^[a-zA-Z]*$/.test(char);
const cx = char.charCodeAt(0);
const CHARS = (!!isChar && cx + num < 123) ? String.fromCharCode(char.charCodeAt(0) + num) : false;
return !!CHARS ? type === 'upper' ? CHARS.toUpperCase() : CHARS : 'Params Error'
},
/**
* @name 扁平化数组 fr : _.Array
*
* @param {Array} arr 数组
*
*/
deepFlatten: arr => _Array.flatten(arr),
/**
* @name 集合排序 fr : _.Collection
*
* @param {String|Function|Array|Object} 排序字段
* @param {String} asc|desc 排序方式
*
*/
orderBy: (arr, iteratees, order = 'asc') => _Collection.orderBy(arr, iteratees, order),
/**
* @name 隐藏手机号中间4位
*
*@param {String} telphone 手机号码
*/
hidePhone: (telphone) => {
const reg = /^(\d{3})\d{4}(\d{4})$/;
return telphone.replace(reg, `$1****$2`)
}
};

openstack gnocchi 服务器硬件监控 (基于Eggjs实现)

openstacklopo1983 发表了文章 • 0 个评论 • 433 次浏览 • 2018-12-22 02:51 • 来自相关话题

'use strict';

const ServerIndex = require('../index')
/**
* @name 获取监控数据
* @param {*} _ucid 用户id
* @param {*} server_id 服务器id
* @param {*} type 聚合类型
* @param {*} metric 聚合数据
* @description type和metric 子属关系如下
*
* instance?["vcpus", "memory", "disk.root.size", "compute.instance.booting.time", "cpu_util", "disk.ephemeral.size", "cpu.delta", "cpu", "memory.usage"]
*
* instance_network_interface ["network.outgoing.packets.rate", "network.incoming.bytes.rate", "network.outgoing.bytes.rate", "network.incoming.packets", "network.incoming.packets.rate", "network.outgoing.bytes", "network.incoming.bytes", "network.outgoing.packets"]
*
* instance_disk ["disk.device.read.bytes.rate", "disk.device.write.requests", "disk.device.write.bytes.rate", "disk.device.write.requests.rate", "disk.device.read.bytes", "disk.device.read.requests", "disk.device.read.requests.rate", "disk.device.write.bytes"]
*
*/
class ResourceService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.actions = ':8041/v1/';
}
async getMeasures(_ucid, { server_id, type, metric } = {}) {
const ctx = this.ctx;
const { subtractMoment } = ctx.helper;
try {
const metricIDs = await this.getmetricID(_ucid, server_id, type, metric);
const getDATASFn = metricIDs.map(async e => {
return {
name: e.name,
datas: await this.OSAJax(`${this.actions}metric/${e.id}/measures`, {
body: { start: subtractMoment(1, 'hour') }
})
}
})
const datas = (await Promise.all(getDATASFn)).reduce((a, b) => {
a[`${b['name']}`] = b['datas'];
return a
}, {})
return { data: datas };
} catch (error) {
console.log(error)
return error
}
}
async getmetricID(_ucid, server_id, type, metric) {
const metrics = metric.split(',')
try {
const DATAS = await this.OSAJax(`${this.actions}search/resource/generic/`, {
_ucid,
method: 'POST',
body: {
"and": [
{
"like": {
"original_resource_id": `%${server_id}%`
}
},
{
"=": {
"type": type
}
}
]
}
});
const { code } = DATAS;
if (!code && !!DATAS.length) {
const MAPS = DATAS[0].metrics
return metrics.reduce((a, b) => { a.push({ name: b, id: MAPS[b] }); return a }, )
} else {
return undefined
}
} catch (error) {
return error
}
}
}

module.exports = ResourceService;[/b]?'use strict';
const Service = require('egg').Service;
//
class indexService extends Service {
constructor(ctx) {
super(ctx);
this.OPTokenMODEL = ctx.model.Openstack.Token;
this.OPUserMODEL = ctx.model.Openstack.Identity.User;
this.OBJKeySort = ctx.helper.OBJKeySort;
const { uri } = this.config.openstack;
this.uri = `${uri}`;
this.CurlOpt = async (method, full, token) => {
return {
'dataType': 'json',
'headers': {
...((!full && !token) && { 'X-Auth-Token': (await ctx.helper.TokenFn('openstack')).data }) || !!token && { 'X-Auth-Token': token },
'Content-Type': 'application/json',
},
'method': method,
...method !== 'DELETE' && { 'data': {} },
'timeout': 60000
}
}
};
/**
* 基础库
*
* @param {Object} body 需要提交的参数
* @param {String} method GET POST DELETE PUT PATCH
* @param {String} params url 参数
* @param {Boolean} full 是否显示完整参数
* @param {String} _ucid 用户ID Optional
*/
async OSAJax(params, { body = {}, method = "GET", full = false, status = false, _ucid } = {}) {
const ctx = this.ctx;
const opt = await this.CurlOpt(method, full, !!_ucid && await this.getUserToken(_ucid));
method !== 'DELETE' && Object.assign(opt.data, { ...!!body ? body : '' });
try {
const result = await ctx.curl(`${this.uri}${!!params ? params : ''}`, opt);
return !!full && result || !!status && result.status || result.data;
} catch (error) {
return error
}
}
}
module.exports = indexService; 查看全部
gnocchi-logo.png
'use strict';

const ServerIndex = require('../index')
/**
* @name 获取监控数据
* @param {*} _ucid 用户id
* @param {*} server_id 服务器id
* @param {*} type 聚合类型
* @param {*} metric 聚合数据
* @description type和metric 子属关系如下
*
* instance?["vcpus", "memory", "disk.root.size", "compute.instance.booting.time", "cpu_util", "disk.ephemeral.size", "cpu.delta", "cpu", "memory.usage"]
*
* instance_network_interface ["network.outgoing.packets.rate", "network.incoming.bytes.rate", "network.outgoing.bytes.rate", "network.incoming.packets", "network.incoming.packets.rate", "network.outgoing.bytes", "network.incoming.bytes", "network.outgoing.packets"]
*
* instance_disk ["disk.device.read.bytes.rate", "disk.device.write.requests", "disk.device.write.bytes.rate", "disk.device.write.requests.rate", "disk.device.read.bytes", "disk.device.read.requests", "disk.device.read.requests.rate", "disk.device.write.bytes"]
*
*/
class ResourceService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.actions = ':8041/v1/';
}
async getMeasures(_ucid, { server_id, type, metric } = {}) {
const ctx = this.ctx;
const { subtractMoment } = ctx.helper;
try {
const metricIDs = await this.getmetricID(_ucid, server_id, type, metric);
const getDATASFn = metricIDs.map(async e => {
return {
name: e.name,
datas: await this.OSAJax(`${this.actions}metric/${e.id}/measures`, {
body: { start: subtractMoment(1, 'hour') }
})
}
})
const datas = (await Promise.all(getDATASFn)).reduce((a, b) => {
a[`${b['name']}`] = b['datas'];
return a
}, {})
return { data: datas };
} catch (error) {
console.log(error)
return error
}
}
async getmetricID(_ucid, server_id, type, metric) {
const metrics = metric.split(',')
try {
const DATAS = await this.OSAJax(`${this.actions}search/resource/generic/`, {
_ucid,
method: 'POST',
body: {
"and": [
{
"like": {
"original_resource_id": `%${server_id}%`
}
},
{
"=": {
"type": type
}
}
]
}
});
const { code } = DATAS;
if (!code && !!DATAS.length) {
const MAPS = DATAS[0].metrics
return metrics.reduce((a, b) => { a.push({ name: b, id: MAPS[b] }); return a }, )
} else {
return undefined
}
} catch (error) {
return error
}
}
}

module.exports = ResourceService;[/b]
?
'use strict';
const Service = require('egg').Service;
//
class indexService extends Service {
constructor(ctx) {
super(ctx);
this.OPTokenMODEL = ctx.model.Openstack.Token;
this.OPUserMODEL = ctx.model.Openstack.Identity.User;
this.OBJKeySort = ctx.helper.OBJKeySort;
const { uri } = this.config.openstack;
this.uri = `${uri}`;
this.CurlOpt = async (method, full, token) => {
return {
'dataType': 'json',
'headers': {
...((!full && !token) && { 'X-Auth-Token': (await ctx.helper.TokenFn('openstack')).data }) || !!token && { 'X-Auth-Token': token },
'Content-Type': 'application/json',
},
'method': method,
...method !== 'DELETE' && { 'data': {} },
'timeout': 60000
}
}
};
/**
* 基础库
*
* @param {Object} body 需要提交的参数
* @param {String} method GET POST DELETE PUT PATCH
* @param {String} params url 参数
* @param {Boolean} full 是否显示完整参数
* @param {String} _ucid 用户ID Optional
*/
async OSAJax(params, { body = {}, method = "GET", full = false, status = false, _ucid } = {}) {
const ctx = this.ctx;
const opt = await this.CurlOpt(method, full, !!_ucid && await this.getUserToken(_ucid));
method !== 'DELETE' && Object.assign(opt.data, { ...!!body ? body : '' });
try {
const result = await ctx.curl(`${this.uri}${!!params ? params : ''}`, opt);
return !!full && result || !!status && result.status || result.data;
} catch (error) {
return error
}
}
}
module.exports = indexService;

queries 分离函数 适用 mongoose

Nodejslopo1983 发表了文章 • 0 个评论 • 318 次浏览 • 2018-10-17 21:34 • 来自相关话题

? // app/extend/helper.js
/**
* GET querys 分离函数
* @param {String} pages 分页 [上一页最后一个数据的_id,分页数量] 分页数量默认为10
* @param {String} fields 需要展示的字段 a,b,c,d
* @param {String} unFields 不需要展示的字段 a,b,c,d
* @param {String} querys 查询条件 如 a=1&b=2&c=3
* @param {String} orderBy 排序字段
* @param {String} sort 排序方式【1:正序,-1:倒序】(必须与prderBy同时出现)
* @param {String} dates 时间范围/时间查询 [开始时间,结束时间]/查询时间
*
* @version 1.0.1
* 2018/10/17 修改返回值类型可直接供mongoose使用
*
* @example
* GET /user/?orderBy=compName&sort=-1&pages=20,1&fields=compName,compAuth,compEmail&unFields=status&dates=2018-8-5&name=lopo
* @return {Object}
* {
* querys: { name: 'lopo' },
* select: { compName: 1, compAuth: 1, compEmail: 1, status: 0 },
* pages: { marker: '1', limit: '20' },
* sort: { compName: '-1' },
* dates: '2018-8-5'
* }
*/
queryParamFn(querys) {
const mapFN = (str) => (field, val) => str[field].split(',').reduce((a, b) => { a<strong> = val; return a }, {});
let strToArr = mapFN(querys);
const pages = !!querys.pages && querys.pages;
const orderBy = !!querys.orderBy && querys.orderBy;
const sort = !!querys.sort && querys.sort;
const dates = !!querys.dates && querys.dates;
const fields = !!querys.fields ? strToArr('fields', 1) : {};
const unFields = !!querys.unFields ? strToArr('unFields', 0) : {};
const dateFn = () => {
if (!!dates) {
const ARR = dates.split(',');
return ARR.length === 2 ? { '$gte': ARR[1], '$lte': ARR[0] } : `${ARR}`
}
}
const sortFn = () => {
let OBJ = {};
if (!!orderBy) {
OBJ[orderBy] = sort;
return OBJ
}
return OBJ || {}
}
delete querys.pages;
delete querys.fields;
delete querys.unFields;
delete querys.orderBy;
delete querys.sort;
delete querys.dates;
return {
'querys': querys || {},
'select': { ...fields, ...unFields },
...(!!pages && {
'pages': {
'marker': pages.split(',')[1] || undefined,
'limit': pages.split(',')[0] || 10
}
}),
...!!orderBy && {
'sort': sortFn()
},
...!!dates && {
'dates': dateFn()
}
}
} 查看全部
?
    // app/extend/helper.js
/**
* GET querys 分离函数
* @param {String} pages 分页 [上一页最后一个数据的_id,分页数量] 分页数量默认为10
* @param {String} fields 需要展示的字段 a,b,c,d
* @param {String} unFields 不需要展示的字段 a,b,c,d
* @param {String} querys 查询条件 如 a=1&b=2&c=3
* @param {String} orderBy 排序字段
* @param {String} sort 排序方式【1:正序,-1:倒序】(必须与prderBy同时出现)
* @param {String} dates 时间范围/时间查询 [开始时间,结束时间]/查询时间
*
* @version 1.0.1
* 2018/10/17 修改返回值类型可直接供mongoose使用
*
* @example
* GET /user/?orderBy=compName&sort=-1&pages=20,1&fields=compName,compAuth,compEmail&unFields=status&dates=2018-8-5&name=lopo
* @return {Object}
* {
* querys: { name: 'lopo' },
* select: { compName: 1, compAuth: 1, compEmail: 1, status: 0 },
* pages: { marker: '1', limit: '20' },
* sort: { compName: '-1' },
* dates: '2018-8-5'
* }
*/
queryParamFn(querys) {
const mapFN = (str) => (field, val) => str[field].split(',').reduce((a, b) => { a<strong> = val; return a }, {});
let strToArr = mapFN(querys);
const pages = !!querys.pages && querys.pages;
const orderBy = !!querys.orderBy && querys.orderBy;
const sort = !!querys.sort && querys.sort;
const dates = !!querys.dates && querys.dates;
const fields = !!querys.fields ? strToArr('fields', 1) : {};
const unFields = !!querys.unFields ? strToArr('unFields', 0) : {};
const dateFn = () => {
if (!!dates) {
const ARR = dates.split(',');
return ARR.length === 2 ? { '$gte': ARR[1], '$lte': ARR[0] } : `${ARR}`
}
}
const sortFn = () => {
let OBJ = {};
if (!!orderBy) {
OBJ[orderBy] = sort;
return OBJ
}
return OBJ || {}
}
delete querys.pages;
delete querys.fields;
delete querys.unFields;
delete querys.orderBy;
delete querys.sort;
delete querys.dates;
return {
'querys': querys || {},
'select': { ...fields, ...unFields },
...(!!pages && {
'pages': {
'marker': pages.split(',')[1] || undefined,
'limit': pages.split(',')[0] || 10
}
}),
...!!orderBy && {
'sort': sortFn()
},
...!!dates && {
'dates': dateFn()
}
}
}

Node+eggjs+mongodb 一步步实现 CRM(2)需求整理

每天进步一点点lopo1983 发表了文章 • 0 个评论 • 379 次浏览 • 2019-03-04 10:00 • 来自相关话题

?























部分附件收限制无法上传(懒得折腾) 请解压缩查看 查看全部
功能结构.png

?
设置.png


资金.png


客户.png


辅助资料.png


部门与员工.png

部分附件收限制无法上传(懒得折腾) 请解压缩查看

Node+eggjs+mongodb 一步步实现 CRM(1)环境搭建

每天进步一点点lopo1983 发表了文章 • 0 个评论 • 502 次浏览 • 2019-02-28 11:24 • 来自相关话题

本文章建议有一定Nodejs开发经验和熟悉ES6/7的开发人员查看,文中若有错误望指出!
?1.安装nodejs?
?
? ?建议选择?LTS 版本,最低要求 8.x(机器配置不好的,请考虑8.x)。
?
2.egg 安装$ npm i egg-init -g
$ egg-init egg-crm --type=simple
$ cd egg-crm
$ npm inpm run devegg文档地址
?
3.mongoDB安装 (4.x)
?
建议在服务器上安装,以便随时可以开发,这里以centos?安装为例
步骤如下:
创建该路径文件 /etc/yum.repos.d/mongodb-org-4.0.repo[mongodb-org-4.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-4.0.asc安装sudo yum install -y mongodb-org-4.0.6 mongodb-org-server-4.0.6 mongodb-org-shell-4.0.6 mongodb-org-mongos-4.0.6 mongodb-org-tools-4.0.6启动service mongod start?具体可参考官方文档
?
4.相关插件配置 "devDependencies": {
"autod": "^3.0.1",
"autod-egg": "^1.1.0",
"baidu-aip-sdk": "^2.3.9",
"bce-sdk-js": "^0.2.9",
"decimal": "^0.0.2",
"egg-bin": "^4.11.0",
"egg-ci": "^1.11.0",
"egg-cors": "^2.1.2",
"egg-jwt": "^3.1.6",
"egg-mock": "^3.21.0",
"egg-mongoose": "^3.1.1",
"egg-validate": "^2.0.2",
"eslint": "^5.13.0",
"eslint-config-egg": "^7.1.0",
"lodash": "^4.17.11",
"stream-to-array": "^2.3.0",
"webstorm-disable-index": "^1.2.0",
"xml2js": "^0.4.19"
},
baidu-aip-sdk:百度AI接口用于智能审核 OCR等
bce-sdk-js: 百度BCE 接口 我们会用到百度的BOS 云存储
decimal:处理JS浮点误差
egg-cors:egg跨域
egg-jwt:egg jsonWebToken
egg-mongoose:mongo数据库链接
egg-validate:egg数据校验
lodash:一个十分优秀的函数编程库
stream-to-array:流处理
xml2js:微信支付

4.egg相关配置
?
config.default.js(相关留空的数据请填入自己的数据)'use strict';

module.exports = appInfo => {
const config = exports = {};

// use for cookie sign key, should change to your own and keep security
config.keys = appInfo.name + '_1539582326426_4353';

// csrf配置
config.security = {
csrf: {
enable: false,
ignoreJSON: true
},
domainWhiteList: ['http://localhost:7001', 'http://192.168.0.123', 'http://localhost]
};
//
config.cors = {
allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS',
credentials: true
};
// // add your config here
// config.middleware = ;
config.middleware = ['errorHandler', 'responseFormatter', 'jwtErrorHandler'];
config.middleware.errorHandler = {
match: '/api',
};
config.middleware.responseFormatter = {
match: '/api',
};
//短信配置
config.sms = {
svip: {
uri: ‘',
name: '',
password: '',
},
};
// 邮箱配置
config.email = {
service: 'QQex',
port: 465,
secureConnection: true,
auth: {
user: '',
pass: '',
}
}
// mongodb配置
config.mongoose = {
url: 'mongodb://127.0.0.1/geecrm',
options: {},
};
// bos云存储
/*****************begin********************/
config.baiduBos = {
endpoint: 'http://bj.bcebos.com',
credentials: {
ak: '',
sk: ''
}
};
config.baiduBosBucket = '';
/*****************end***********************/
// baidu AIP
config.baiduAIP = {
APP_ID: '',
API_KEY: '',
SECRET_KEY: ''
};
// baidu VOD
config.baiduVod = {
endpoint: 'http://vod.bj.baidubce.com',
credentials: {
ak: '',
sk: ''
}
};
// 企业微信配置
config.weWork = {
'corpid': '',
'corpsecret': '',
'agentId': ''
};
// 微信配置
config.wechatApi = {
appId: '',
appSecret: '',
};
// 默认json 设置
config.JSONS = {
'code': 200,
'message': 'success',
'uri': 'https://api.lanjing.xyz',
};
config.alipay = {
appId: "",
rsaPrivate: "",
notifyUrl: "", //异步回调
signType: "RSA2",
rsaPublic: "",
sandbox: false //沙箱环境
}

config.wechatPay = {
partnerKey: "",
appId: "",
mchId: "",
notifyUrl: "http://www.langjing.xyz/wechat/notify", //异步回调,到微信平台设置下
pfx: ""
}
//
return config;
};
??plugin.js ? ? ? ? ?'use strict';
exports.validate = {
enable: true,
package: 'egg-validate',
};
exports.mongoose = {
enable: true,
package: 'egg-mongoose',
};
exports.jwt = {
enable: true,
package: "egg-jwt"
};
exports.cors = {
enable: true,
package: "egg-cors"
};

?
?项目目录结构 (文件夹可按需建立无内容留空即可)

?





? 查看全部

本文章建议有一定Nodejs开发经验和熟悉ES6/7的开发人员查看,文中若有错误望指出!


?1.安装nodejs?
?
? ?建议选择?LTS 版本,最低要求 8.x(机器配置不好的,请考虑8.x)。
?
2.egg 安装
$ npm i egg-init -g
$ egg-init egg-crm --type=simple
$ cd egg-crm
$ npm i
npm run dev
egg文档地址
?
3.mongoDB安装 (4.x)
?
建议在服务器上安装,以便随时可以开发,这里以centos?安装为例
步骤如下:
创建该路径文件 /etc/yum.repos.d/mongodb-org-4.0.repo
[mongodb-org-4.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-4.0.asc
安装
sudo yum install -y mongodb-org-4.0.6 mongodb-org-server-4.0.6 mongodb-org-shell-4.0.6 mongodb-org-mongos-4.0.6 mongodb-org-tools-4.0.6
启动
service mongod start
?具体可参考官方文档
?
4.相关插件配置
  "devDependencies": {
"autod": "^3.0.1",
"autod-egg": "^1.1.0",
"baidu-aip-sdk": "^2.3.9",
"bce-sdk-js": "^0.2.9",
"decimal": "^0.0.2",
"egg-bin": "^4.11.0",
"egg-ci": "^1.11.0",
"egg-cors": "^2.1.2",
"egg-jwt": "^3.1.6",
"egg-mock": "^3.21.0",
"egg-mongoose": "^3.1.1",
"egg-validate": "^2.0.2",
"eslint": "^5.13.0",
"eslint-config-egg": "^7.1.0",
"lodash": "^4.17.11",
"stream-to-array": "^2.3.0",
"webstorm-disable-index": "^1.2.0",
"xml2js": "^0.4.19"
},

baidu-aip-sdk:百度AI接口用于智能审核 OCR等
bce-sdk-js: 百度BCE 接口 我们会用到百度的BOS 云存储
decimal:处理JS浮点误差
egg-cors:egg跨域
egg-jwt:egg jsonWebToken
egg-mongoose:mongo数据库链接
egg-validate:egg数据校验
lodash:一个十分优秀的函数编程库
stream-to-array:流处理
xml2js:微信支付

4.egg相关配置
?
config.default.js(相关留空的数据请填入自己的数据)
'use strict';

module.exports = appInfo => {
const config = exports = {};

// use for cookie sign key, should change to your own and keep security
config.keys = appInfo.name + '_1539582326426_4353';

// csrf配置
config.security = {
csrf: {
enable: false,
ignoreJSON: true
},
domainWhiteList: ['http://localhost:7001', 'http://192.168.0.123', 'http://localhost]
};
//
config.cors = {
allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS',
credentials: true
};
// // add your config here
// config.middleware = ;
config.middleware = ['errorHandler', 'responseFormatter', 'jwtErrorHandler'];
config.middleware.errorHandler = {
match: '/api',
};
config.middleware.responseFormatter = {
match: '/api',
};
//短信配置
config.sms = {
svip: {
uri: ‘',
name: '',
password: '',
},
};
// 邮箱配置
config.email = {
service: 'QQex',
port: 465,
secureConnection: true,
auth: {
user: '',
pass: '',
}
}
// mongodb配置
config.mongoose = {
url: 'mongodb://127.0.0.1/geecrm',
options: {},
};
// bos云存储
/*****************begin********************/
config.baiduBos = {
endpoint: 'http://bj.bcebos.com',
credentials: {
ak: '',
sk: ''
}
};
config.baiduBosBucket = '';
/*****************end***********************/
// baidu AIP
config.baiduAIP = {
APP_ID: '',
API_KEY: '',
SECRET_KEY: ''
};
// baidu VOD
config.baiduVod = {
endpoint: 'http://vod.bj.baidubce.com',
credentials: {
ak: '',
sk: ''
}
};
// 企业微信配置
config.weWork = {
'corpid': '',
'corpsecret': '',
'agentId': ''
};
// 微信配置
config.wechatApi = {
appId: '',
appSecret: '',
};
// 默认json 设置
config.JSONS = {
'code': 200,
'message': 'success',
'uri': 'https://api.lanjing.xyz',
};
config.alipay = {
appId: "",
rsaPrivate: "",
notifyUrl: "", //异步回调
signType: "RSA2",
rsaPublic: "",
sandbox: false //沙箱环境
}

config.wechatPay = {
partnerKey: "",
appId: "",
mchId: "",
notifyUrl: "http://www.langjing.xyz/wechat/notify", //异步回调,到微信平台设置下
pfx: ""
}
//
return config;
};

??plugin.js ? ? ? ? ?
'use strict';
exports.validate = {
enable: true,
package: 'egg-validate',
};
exports.mongoose = {
enable: true,
package: 'egg-mongoose',
};
exports.jwt = {
enable: true,
package: "egg-jwt"
};
exports.cors = {
enable: true,
package: "egg-cors"
};


?
?项目目录结构 (文件夹可按需建立无内容留空即可)

?
QQ图片20190304101503.png


?

eggjs 上传文件

Nodejslopo1983 发表了文章 • 0 个评论 • 206 次浏览 • 2019-08-30 17:52 • 来自相关话题

'use strict';
const Controller = require('egg').Controller;
// 文件存储
const fs = require('fs');
const path = require('path');
const awaitWriteStream = require('await-stream-ready').write;
const sendToWormhole = require('stream-wormhole');

class UploadController extends Controller {
/**
*
* @param {*} stream 传入的Buffer流
* @param {*} paths 保存的路径
* @param {*} multiple 是否多文件
* @param {*} files 多文件返回
*/
async upload(stream, paths = "app/public/img", multiple = false, files = []) {
const filename = Math.random().toString(36).substr(2) + new Date().getTime() + path.extname(stream.filename).toLocaleLowerCase();
const target = path.join(this.config.baseDir, paths, filename);
const writeStream = fs.createWriteStream(target);
try {
await awaitWriteStream(stream.pipe(writeStream));
return !!multiple ? files.push(filename) : filename;
} catch (err) {
await sendToWormhole(stream);
return { code: 422, message: '上传失败,请重试!' }
}
};
// 单文件
async create() {
const ctx = this.ctx;
// 获取流
const stream = await ctx.getFileStream();
// 生成文件名
const filename = Math.random().toString(36).substr(2) + new Date().getTime() + path.extname(stream.filename).toLocaleLowerCase();
// pipe流写入信息
const target = path.join(this.config.baseDir, 'app/public/img', filename);
const writeStream = fs.createWriteStream(target);
try {
// 保存
await awaitWriteStream(stream.pipe(writeStream));
} catch (err) {
// 保存失败销毁stream 不然接口会pending到超时
await sendToWormhole(stream);
this.ctx.body = { code: 422, message: '上传失败,请重试!' }
throw err;
}
this.ctx.body = {
data: filename
};
};
// 多文件
async creates() {
const ctx = this.ctx;
// 获取文件流组
const streams = ctx.multipart();
let stream;
// 保存返回的文件信息
let files = [];
// 其他form 参数
let fields = {}
while ((stream = await streams()) != null) {
// 检查是否有其他参数 如果有写入 这里做案例 不做处理
if (stream.length) {
fields[stream[0]] = stream[1]
} else {
// 空文件处理
if (!stream.filename) {
return;
}
// 设置文件名称
const filename = Math.random().toString(36).substr(2) + new Date().getTime() + path.extname(stream.filename).toLocaleLowerCase();
// pipe 设置
const target = path.join(this.config.baseDir, 'app/public/img', filename);
const writeStream = fs.createWriteStream(target);
try {
// 保存
await awaitWriteStream(stream.pipe(writeStream));
// 写入数组
files.push({ filename, path: `/img/${filename}` })
} catch (err) {
await sendToWormhole(stream);
this.ctx.body = { code: 422, message: '上传失败,请重试!' }
throw err;
}
}
}
this.ctx.body = {
data: { files, fields }
};
}
}

module.exports = UploadController 查看全部
'use strict';
const Controller = require('egg').Controller;
// 文件存储
const fs = require('fs');
const path = require('path');
const awaitWriteStream = require('await-stream-ready').write;
const sendToWormhole = require('stream-wormhole');

class UploadController extends Controller {
/**
*
* @param {*} stream 传入的Buffer流
* @param {*} paths 保存的路径
* @param {*} multiple 是否多文件
* @param {*} files 多文件返回
*/
async upload(stream, paths = "app/public/img", multiple = false, files = []) {
const filename = Math.random().toString(36).substr(2) + new Date().getTime() + path.extname(stream.filename).toLocaleLowerCase();
const target = path.join(this.config.baseDir, paths, filename);
const writeStream = fs.createWriteStream(target);
try {
await awaitWriteStream(stream.pipe(writeStream));
return !!multiple ? files.push(filename) : filename;
} catch (err) {
await sendToWormhole(stream);
return { code: 422, message: '上传失败,请重试!' }
}
};
// 单文件
async create() {
const ctx = this.ctx;
// 获取流
const stream = await ctx.getFileStream();
// 生成文件名
const filename = Math.random().toString(36).substr(2) + new Date().getTime() + path.extname(stream.filename).toLocaleLowerCase();
// pipe流写入信息
const target = path.join(this.config.baseDir, 'app/public/img', filename);
const writeStream = fs.createWriteStream(target);
try {
// 保存
await awaitWriteStream(stream.pipe(writeStream));
} catch (err) {
// 保存失败销毁stream 不然接口会pending到超时
await sendToWormhole(stream);
this.ctx.body = { code: 422, message: '上传失败,请重试!' }
throw err;
}
this.ctx.body = {
data: filename
};
};
// 多文件
async creates() {
const ctx = this.ctx;
// 获取文件流组
const streams = ctx.multipart();
let stream;
// 保存返回的文件信息
let files = [];
// 其他form 参数
let fields = {}
while ((stream = await streams()) != null) {
// 检查是否有其他参数 如果有写入 这里做案例 不做处理
if (stream.length) {
fields[stream[0]] = stream[1]
} else {
// 空文件处理
if (!stream.filename) {
return;
}
// 设置文件名称
const filename = Math.random().toString(36).substr(2) + new Date().getTime() + path.extname(stream.filename).toLocaleLowerCase();
// pipe 设置
const target = path.join(this.config.baseDir, 'app/public/img', filename);
const writeStream = fs.createWriteStream(target);
try {
// 保存
await awaitWriteStream(stream.pipe(writeStream));
// 写入数组
files.push({ filename, path: `/img/${filename}` })
} catch (err) {
await sendToWormhole(stream);
this.ctx.body = { code: 422, message: '上传失败,请重试!' }
throw err;
}
}
}
this.ctx.body = {
data: { files, fields }
};
}
}

module.exports = UploadController

Node+eggjs+mongodb 一步步实现 CRM(2)需求整理

每天进步一点点lopo1983 发表了文章 • 0 个评论 • 379 次浏览 • 2019-03-04 10:00 • 来自相关话题

?























部分附件收限制无法上传(懒得折腾) 请解压缩查看 查看全部
功能结构.png

?
设置.png


资金.png


客户.png


辅助资料.png


部门与员工.png

部分附件收限制无法上传(懒得折腾) 请解压缩查看

Node+eggjs+mongodb 一步步实现 CRM(1)环境搭建

每天进步一点点lopo1983 发表了文章 • 0 个评论 • 502 次浏览 • 2019-02-28 11:24 • 来自相关话题

本文章建议有一定Nodejs开发经验和熟悉ES6/7的开发人员查看,文中若有错误望指出!
?1.安装nodejs?
?
? ?建议选择?LTS 版本,最低要求 8.x(机器配置不好的,请考虑8.x)。
?
2.egg 安装$ npm i egg-init -g
$ egg-init egg-crm --type=simple
$ cd egg-crm
$ npm inpm run devegg文档地址
?
3.mongoDB安装 (4.x)
?
建议在服务器上安装,以便随时可以开发,这里以centos?安装为例
步骤如下:
创建该路径文件 /etc/yum.repos.d/mongodb-org-4.0.repo[mongodb-org-4.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-4.0.asc安装sudo yum install -y mongodb-org-4.0.6 mongodb-org-server-4.0.6 mongodb-org-shell-4.0.6 mongodb-org-mongos-4.0.6 mongodb-org-tools-4.0.6启动service mongod start?具体可参考官方文档
?
4.相关插件配置 "devDependencies": {
"autod": "^3.0.1",
"autod-egg": "^1.1.0",
"baidu-aip-sdk": "^2.3.9",
"bce-sdk-js": "^0.2.9",
"decimal": "^0.0.2",
"egg-bin": "^4.11.0",
"egg-ci": "^1.11.0",
"egg-cors": "^2.1.2",
"egg-jwt": "^3.1.6",
"egg-mock": "^3.21.0",
"egg-mongoose": "^3.1.1",
"egg-validate": "^2.0.2",
"eslint": "^5.13.0",
"eslint-config-egg": "^7.1.0",
"lodash": "^4.17.11",
"stream-to-array": "^2.3.0",
"webstorm-disable-index": "^1.2.0",
"xml2js": "^0.4.19"
},
baidu-aip-sdk:百度AI接口用于智能审核 OCR等
bce-sdk-js: 百度BCE 接口 我们会用到百度的BOS 云存储
decimal:处理JS浮点误差
egg-cors:egg跨域
egg-jwt:egg jsonWebToken
egg-mongoose:mongo数据库链接
egg-validate:egg数据校验
lodash:一个十分优秀的函数编程库
stream-to-array:流处理
xml2js:微信支付

4.egg相关配置
?
config.default.js(相关留空的数据请填入自己的数据)'use strict';

module.exports = appInfo => {
const config = exports = {};

// use for cookie sign key, should change to your own and keep security
config.keys = appInfo.name + '_1539582326426_4353';

// csrf配置
config.security = {
csrf: {
enable: false,
ignoreJSON: true
},
domainWhiteList: ['http://localhost:7001', 'http://192.168.0.123', 'http://localhost]
};
//
config.cors = {
allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS',
credentials: true
};
// // add your config here
// config.middleware = ;
config.middleware = ['errorHandler', 'responseFormatter', 'jwtErrorHandler'];
config.middleware.errorHandler = {
match: '/api',
};
config.middleware.responseFormatter = {
match: '/api',
};
//短信配置
config.sms = {
svip: {
uri: ‘',
name: '',
password: '',
},
};
// 邮箱配置
config.email = {
service: 'QQex',
port: 465,
secureConnection: true,
auth: {
user: '',
pass: '',
}
}
// mongodb配置
config.mongoose = {
url: 'mongodb://127.0.0.1/geecrm',
options: {},
};
// bos云存储
/*****************begin********************/
config.baiduBos = {
endpoint: 'http://bj.bcebos.com',
credentials: {
ak: '',
sk: ''
}
};
config.baiduBosBucket = '';
/*****************end***********************/
// baidu AIP
config.baiduAIP = {
APP_ID: '',
API_KEY: '',
SECRET_KEY: ''
};
// baidu VOD
config.baiduVod = {
endpoint: 'http://vod.bj.baidubce.com',
credentials: {
ak: '',
sk: ''
}
};
// 企业微信配置
config.weWork = {
'corpid': '',
'corpsecret': '',
'agentId': ''
};
// 微信配置
config.wechatApi = {
appId: '',
appSecret: '',
};
// 默认json 设置
config.JSONS = {
'code': 200,
'message': 'success',
'uri': 'https://api.lanjing.xyz',
};
config.alipay = {
appId: "",
rsaPrivate: "",
notifyUrl: "", //异步回调
signType: "RSA2",
rsaPublic: "",
sandbox: false //沙箱环境
}

config.wechatPay = {
partnerKey: "",
appId: "",
mchId: "",
notifyUrl: "http://www.langjing.xyz/wechat/notify", //异步回调,到微信平台设置下
pfx: ""
}
//
return config;
};
??plugin.js ? ? ? ? ?'use strict';
exports.validate = {
enable: true,
package: 'egg-validate',
};
exports.mongoose = {
enable: true,
package: 'egg-mongoose',
};
exports.jwt = {
enable: true,
package: "egg-jwt"
};
exports.cors = {
enable: true,
package: "egg-cors"
};

?
?项目目录结构 (文件夹可按需建立无内容留空即可)

?





? 查看全部

本文章建议有一定Nodejs开发经验和熟悉ES6/7的开发人员查看,文中若有错误望指出!


?1.安装nodejs?
?
? ?建议选择?LTS 版本,最低要求 8.x(机器配置不好的,请考虑8.x)。
?
2.egg 安装
$ npm i egg-init -g
$ egg-init egg-crm --type=simple
$ cd egg-crm
$ npm i
npm run dev
egg文档地址
?
3.mongoDB安装 (4.x)
?
建议在服务器上安装,以便随时可以开发,这里以centos?安装为例
步骤如下:
创建该路径文件 /etc/yum.repos.d/mongodb-org-4.0.repo
[mongodb-org-4.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-4.0.asc
安装
sudo yum install -y mongodb-org-4.0.6 mongodb-org-server-4.0.6 mongodb-org-shell-4.0.6 mongodb-org-mongos-4.0.6 mongodb-org-tools-4.0.6
启动
service mongod start
?具体可参考官方文档
?
4.相关插件配置
  "devDependencies": {
"autod": "^3.0.1",
"autod-egg": "^1.1.0",
"baidu-aip-sdk": "^2.3.9",
"bce-sdk-js": "^0.2.9",
"decimal": "^0.0.2",
"egg-bin": "^4.11.0",
"egg-ci": "^1.11.0",
"egg-cors": "^2.1.2",
"egg-jwt": "^3.1.6",
"egg-mock": "^3.21.0",
"egg-mongoose": "^3.1.1",
"egg-validate": "^2.0.2",
"eslint": "^5.13.0",
"eslint-config-egg": "^7.1.0",
"lodash": "^4.17.11",
"stream-to-array": "^2.3.0",
"webstorm-disable-index": "^1.2.0",
"xml2js": "^0.4.19"
},

baidu-aip-sdk:百度AI接口用于智能审核 OCR等
bce-sdk-js: 百度BCE 接口 我们会用到百度的BOS 云存储
decimal:处理JS浮点误差
egg-cors:egg跨域
egg-jwt:egg jsonWebToken
egg-mongoose:mongo数据库链接
egg-validate:egg数据校验
lodash:一个十分优秀的函数编程库
stream-to-array:流处理
xml2js:微信支付

4.egg相关配置
?
config.default.js(相关留空的数据请填入自己的数据)
'use strict';

module.exports = appInfo => {
const config = exports = {};

// use for cookie sign key, should change to your own and keep security
config.keys = appInfo.name + '_1539582326426_4353';

// csrf配置
config.security = {
csrf: {
enable: false,
ignoreJSON: true
},
domainWhiteList: ['http://localhost:7001', 'http://192.168.0.123', 'http://localhost]
};
//
config.cors = {
allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS',
credentials: true
};
// // add your config here
// config.middleware = ;
config.middleware = ['errorHandler', 'responseFormatter', 'jwtErrorHandler'];
config.middleware.errorHandler = {
match: '/api',
};
config.middleware.responseFormatter = {
match: '/api',
};
//短信配置
config.sms = {
svip: {
uri: ‘',
name: '',
password: '',
},
};
// 邮箱配置
config.email = {
service: 'QQex',
port: 465,
secureConnection: true,
auth: {
user: '',
pass: '',
}
}
// mongodb配置
config.mongoose = {
url: 'mongodb://127.0.0.1/geecrm',
options: {},
};
// bos云存储
/*****************begin********************/
config.baiduBos = {
endpoint: 'http://bj.bcebos.com',
credentials: {
ak: '',
sk: ''
}
};
config.baiduBosBucket = '';
/*****************end***********************/
// baidu AIP
config.baiduAIP = {
APP_ID: '',
API_KEY: '',
SECRET_KEY: ''
};
// baidu VOD
config.baiduVod = {
endpoint: 'http://vod.bj.baidubce.com',
credentials: {
ak: '',
sk: ''
}
};
// 企业微信配置
config.weWork = {
'corpid': '',
'corpsecret': '',
'agentId': ''
};
// 微信配置
config.wechatApi = {
appId: '',
appSecret: '',
};
// 默认json 设置
config.JSONS = {
'code': 200,
'message': 'success',
'uri': 'https://api.lanjing.xyz',
};
config.alipay = {
appId: "",
rsaPrivate: "",
notifyUrl: "", //异步回调
signType: "RSA2",
rsaPublic: "",
sandbox: false //沙箱环境
}

config.wechatPay = {
partnerKey: "",
appId: "",
mchId: "",
notifyUrl: "http://www.langjing.xyz/wechat/notify", //异步回调,到微信平台设置下
pfx: ""
}
//
return config;
};

??plugin.js ? ? ? ? ?
'use strict';
exports.validate = {
enable: true,
package: 'egg-validate',
};
exports.mongoose = {
enable: true,
package: 'egg-mongoose',
};
exports.jwt = {
enable: true,
package: "egg-jwt"
};
exports.cors = {
enable: true,
package: "egg-cors"
};


?
?项目目录结构 (文件夹可按需建立无内容留空即可)

?
QQ图片20190304101503.png


?

Nodejs 常用helper函数(持续更新)

Nodejslopo1983 发表了文章 • 0 个评论 • 314 次浏览 • 2018-12-29 13:38 • 来自相关话题

const crypto = require('crypto');
const toArray = require('stream-to-array');
const sendToWormhole = require('stream-wormhole');
const moment = require('moment');
const Decimal = require('decimal');
const _Array = require('lodash/array')
const _Collection = require('lodash/collection')
Date.prototype.format = function () {
let s = '';
s += this.getFullYear() + '-';
s += (this.getMonth() + 1) + "-";
s += this.getDate();
return (s);
};
module.exports = {
/**
* GET querys 分离函数
* @param {String} pages 分页 [上一页最后一个数据的_id,分页数量] 分页数量默认为10
* @param {String} fields 需要展示的字段 a,b,c,d
* @param {String} unFields 不需要展示的字段 a,b,c,d
* @param {String} querys 查询条件 如 a=1&b=2&c=3
* @param {String} orderBy 排序字段
* @param {String} sort 排序方式【1:正序,-1:倒序】(必须与orderBy同时出现)
* @param {String} dates 时间范围/时间查询 [开始时间,结束时间]/查询时间
*
* @version 1.0.1
* 2018/10/17 修改返回值类型可直接供mongoose使用
*
* @example
* GET /user/?orderBy=compName&sort=-1&pages=20,1&fields=compName,compAuth,compEmail&unFields=status&dates=2018-8-5&name=lopo
* index(){
* const {ctx} = this;
* const {queryParamFn} = ctx.helper;
* ctx.body = queryParamFn(ctx.query);
* }
*
* @return {Object}
* {
* querys: { name: 'lopo' },
* select: { compName: 1, compAuth: 1, compEmail: 1, status: 0 },
* pages: { marker: '1', limit: '20' },
* sort: { compName: '-1' },
* dates: '2018-8-5'
* }
*/
queryParamFn(querys, db = false) {
const mapFN = (str) => (field, val) => str[field].split(',').reduce((a, b) => {
a[b] = val;
return a
}, {});
let strToArr = mapFN(querys);
const sort = !!querys.sort ? JSON.parse(querys.sort) : {};
// const dates = querys.dates || [];
const fields = !!querys.fields ? strToArr('fields', 1) : {};
const unFields = !!querys.unFields ? strToArr('unFields', 0) : {};
const limit = !!querys.limit ? querys.limit * 1 : 10;
const marker = querys.marker || ''
const dateFn = () => {
if (!!dates) {
const ARR = dates.split(',');
return ARR.length === 2 ? {
'$gte': ARR[1],
'$lte': ARR[0]
} : `${ARR}`
}
}
delete querys.pages;
delete querys.fields;
delete querys.unFields;
delete querys.sort;
delete querys.dates;
delete querys.marker;
delete querys.limit;
delete querys._;
delete querys._type;
return {
'querys': {
...querys,
} || {},
'select': {
...fields,
...unFields
},
limit,
marker,
sort,
// ...!!dates && {
// 'dates': dateFn()
// }
}
},
/**
* PD分离
* @param {String} psd PD
* @param {String} pilipala chicken
*
* @return {String} 返回分离后的字符串
*/
bilibole(psd, pilipala) {
let bilibole = crypto.createHash('md5').update(`${psd}:${pilipala}`).digest('hex');
return `${pilipala}${bilibole}`;
},
/**
* @name stream 转buffer
*
* @param {files} stream 文件流
* @param {Function} fn 文件处理函数
*
* @return {String:Buffer} 返回Buferr 可用函数对Buffer进行处理后返回
*/
async stream2buf(stream, fn) {
try {
const parts = await toArray(stream);
const buf = Buffer.concat(parts);
return !!fn ? fn(buf) : buf
} catch (err) {
await sendToWormhole(stream);
throw err;
};
},
/**
* len 随机位数 默认6位
* @param {Number} len 随机数字长度 默认为6
*/
roundCode: (len = 6) => {
let pwd = "";
for (let idx = 0; idx < len; idx++) {
let seed = parseInt(Math.random() * 9);
pwd += seed;
}
return pwd;
},
/**
* 获取token函数
* @param {String} type token类型 ['BAIDUAip','WEWork','WeiXin','DirectAdmin','openstack']
*/
async TokenFn(type) {
const {
ctx,
config
} = this;
let datas;
const DirectAdmin = config.vhost.DirectAdmin;
const wechatApi = config.wechatApi;
const openstackApi = config.openstack;
const tokenMapFn = async () => {
const maps = {
// 百度token
'BAIDUAip': await ctx.curl('aip.baidubce.com/oauth/2.0/token', {
method: 'POST',
dataType: 'json',
data: {
'grant_type': 'client_credentials',
'client_id': config.baiduAIP.API_KEY,
'client_secret': config.baiduAIP.SECRET_KEY
}
}),
// 企业微信token
'WEWork': await ctx.curl('https://qyapi.weixin.qq.com/cgi-bin/gettoken', {
dataType: 'json',
data: {
'corpid': config.weWork.corpid,
'corpsecret': config.weWork.corpsecret
}
}),
// 微信token
'WeiXin': await ctx.curl('https://api.weixin.qq.com/cgi-bin/token', {
dataType: 'json',
data: {
'grant_type': 'client_credential',
'appid': wechatApi.appId,
'secret': wechatApi.appSecret
}
}),
// // DA token
// 'DirectAdmin': await ctx.curl(`${DirectAdmin.server}:${DirectAdmin.port}/CMD_LOGIN`, {
// method: 'POST',
// data: {
// 'username': DirectAdmin.username,
// 'password': DirectAdmin.password
// }
// }),
// openstack token
'openstack': await ctx.curl(`${openstackApi.uri}:5000/v3/auth/tokens?nocatalog`, {
method: 'POST',
dataType: 'json',
headers: {
'Content-Type': 'application/json',
},
content: `{
"auth": {
"identity": {
"methods": ["password"],
"password": {
"user": {
"name": "${openstackApi.name}",
"password": "${openstackApi.password}",
"domain": { "name": "Default" }
}
}
},
"scope": {
"project": {
"domain": {
"id": "default"
},
"name": "admin"
}
}
}
}`,
})
};
return maps[type]
};
const setResult = async () => {
if (type !== 'DirectAdmin') {
const data = await tokenMapFn();
return {
tokenValue: type === 'openstack' ? data.headers['x-subject-token'] : data.data.access_token,
tokenType: type,
tokenExpiresIn: type === 'openstack' ? new Date(data.data.token.expires_at) - 0 - 60 * 1000 : data.data.expires_in * 1000 + (new Date() - 0)
}
} else {
const cookies = (await tokenMapFn()).res.headers['set-cookie'][0].split(';');
return {
tokenValue: cookies[0],
tokenType: 'DirectAdmin',
tokenExpiresIn: (new Date(cookies[2].split('=')[1]) - 0) + 8 * 60 * 60 * 1000
}
}
};
const setToken = async () => {
const saveToken = await ctx.model.Tokens.findOneAndUpdate({
'tokenType': type
}, { ...(await setResult())
}, {
upsert: true,
new: true
});
datas = saveToken.tokenValue;
};
const tokens = await ctx.model.Tokens.findOne({
'tokenType': type
});
!!tokens && tokens.tokenExpiresIn > (new Date - 0) ? datas = tokens.tokenValue : await setToken();
return {
data: datas
}
},
/**
* mongo报错处理
*/
mongoError(error) {
const errorMap = {
'11000': ''
}
},
/**
* 函数柯里化
*/
curry: (fn, arity = fn.length, ...args) => arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args),
/**
* moment mix
* @param {String} scope [/day/week/month/quarter/year] 日/周/月/季度/年
* @int {Number} int 数值
*/
// 当前时间
getThisMoment: (scope) => {
return {
startDate: moment().startOf(scope).format(),
endDate: moment().format()
}
},
// 之前
getPrevMoment: (scope) => {
return {
startDate: moment().week(moment()[scope]() - 1).startOf(scope).format(),
endDate: moment().week(moment()[scope]() - 1).endOf(scope).format()
}
},
// 时间差 type [add,subtract]
subtractMoment: (int, scope, type = "subtract", withFormat = true, fn) => {
const dates = moment()[type](int, scope);
return !fn ? (withFormat ? dates.format() : dates) : fn(dates);
},
/**
* @name 格式化时间
* @param {String} formater 格式化输出标准 YYYY/MM/DD/HH/mm/ss/SSS
* @param {Date} dates 日期
* @param {Boolean} utc UTC
*/

formatDate: ({
formater,
dates
} = {}, utc = false) => {
const DATE = moment(!!dates ? dates : new Date());
return utc ? DATE.utc().format(!!formater && formater) : DATE.format(!!formater && formater)
},
// 生成时间数组
getDayAll: (begin, end) => {
let dateAllArr = new Array();
let ab = moment(begin).format("YYYY-MM-DD").split("-");
const ae = moment(end).format("YYYY-MM-DD").split("-");
const db = new Date();
db.setUTCFullYear(ab[0], ab[1] - 1, ab[2]);
const de = new Date();
de.setUTCFullYear(ae[0], ae[1] - 1, ae[2]);
const unixDb = db.getTime();
const unixDe = de.getTime();
for (let k = unixDb; k <= unixDe;) {
dateAllArr.push(new Date(parseInt(k)).format().toString());
k = k + 24 * 60 * 60 * 1000;
}
return dateAllArr;
},
getCTX: () => app.createAnonymousContext(),
// 发送短信,邮件,企业微信通知
async sendWarning(ctx, content, _id, compName) {
const auth = ctx.state.user;
const {
compEmail,
compPhone
} = await ctx.model.Company.findOne({
'_id': _id
}, {
'compEmail': 1,
'compPhone': 1
});
// await ctx.service.wework.message.send({ totag: 2, content: content.replace(/您/g, `${compName}`) });
await ctx.service.sms.svipwang.create(compPhone, content);
await ctx.service.sms.mail.sendMailDefault({
subject: '系统消息通知',
to: compEmail,
html: content
});
},
// 清理paramsFN()
deleteUndefined: (object) => {
for (let key in object) {
key !== 'detail' && delete object[(object[key] === undefined || object[key] === '' || object[key] === '[]') && key]
}
return object
},
/******************************************************************************************/
/**
* class DistributedEdit extends mix(class1, class2)
*/
// mixins
mix: (...mixins) => {
class Mix {}

for (let mixin of mixins) {
copyProperties(Mix.prototype, mixin); // 拷贝实例属性
copyProperties(Mix.prototype, Reflect.getPrototypeOf(mixin)); // 拷贝原型属性
}

return Mix;
},
// mixinsParams
copyProperties: (target, source) => {
for (let key of Reflect.ownKeys(source)) {
if (key !== "constructor" &&
key !== "prototype" &&
key !== "name"
) {
let desc = Object.getOwnPropertyDescriptor(source, key);
Object.defineProperty(target, key, desc);
}
}
},
/***************end****************/
/**
* 货币计算 mix
* @param {String} method [add/+,sub/-,mul/*,div//] 加减乘除
* @param {Array} money 需要计算的数字【前,后】
*/
decimalCash: (method = "add", money) => {
return new Decimal(money[0])[method](new Decimal(money[1])).toNumber()
},
// decimaFN: () => { return this.decimalCash() },
// decimalAdd: () => { return this.decimaFN('add') },
// decimalSub: () => { return this.decimaFN('sub') },
// decimalMul: () => { return this.decimaFN('mul') },
// decimalDiv: () => { return this.decimaFN('div') },
/****************end***************/
/**
* MONGO type转换
* @param {Array} ARR 字符串字典
* @param {Number} value 值
* @param {Boolean} hasZero 是否从0开始
*/
NumToStr(ARR, value, hasZero = false) {
return `${value}|${ARR[hasZero ? value : value - 1]}`
},
/**
* @name mongoDB update数组参数
*
*/
objToQuery(OBJ, KEY) {
return Object.keys(OBJ).reduce((a, b) => {
a[`${KEY}.$.${b}`] = OBJ[b];
return a
}, {});
},
/**
* @name 线转树
* @param {Array} ARR 需要处理的数组
* @param {String} keyName 作为分类的字段
*/
Array2Object: (ARR, keyName) => {
return ARR.reduce((a, b) => {
const keys = b[keyName];
if (a[keys] === undefined) a[keys] = [];
a[keys].push(b);
return a;
}, {})
},
/**
* @name 字典排序
*/
OBJKeySort: (obj) => {
const newkey = Object.keys(obj).sort();
const newObj = {};
for (var i = 0; i < newkey.length; i++) {
newObj[newkey[i]] = obj[newkey[i]];
}
return newObj;
},
/**
* @name 字母升序
*
* @param {String} chars 字母
* @param {Number} num 进几位
*
*/
nextChars: (chars, num, type = 'lower') => {
const char = chars.toLowerCase();
const isChar = /^[a-zA-Z]*$/.test(char);
const cx = char.charCodeAt(0);
const CHARS = (!!isChar && cx + num < 123) ? String.fromCharCode(char.charCodeAt(0) + num) : false;
return !!CHARS ? type === 'upper' ? CHARS.toUpperCase() : CHARS : 'Params Error'
},
/**
* @name 扁平化数组 fr : _.Array
*
* @param {Array} arr 数组
*
*/
deepFlatten: arr => _Array.flatten(arr),
/**
* @name 集合排序 fr : _.Collection
*
* @param {String|Function|Array|Object} 排序字段
* @param {String} asc|desc 排序方式
*
*/
orderBy: (arr, iteratees, order = 'asc') => _Collection.orderBy(arr, iteratees, order),
/**
* @name 隐藏手机号中间4位
*
*@param {String} telphone 手机号码
*/
hidePhone: (telphone) => {
const reg = /^(\d{3})\d{4}(\d{4})$/;
return telphone.replace(reg, `$1****$2`)
}
}; 查看全部
const crypto = require('crypto');
const toArray = require('stream-to-array');
const sendToWormhole = require('stream-wormhole');
const moment = require('moment');
const Decimal = require('decimal');
const _Array = require('lodash/array')
const _Collection = require('lodash/collection')
Date.prototype.format = function () {
let s = '';
s += this.getFullYear() + '-';
s += (this.getMonth() + 1) + "-";
s += this.getDate();
return (s);
};
module.exports = {
/**
* GET querys 分离函数
* @param {String} pages 分页 [上一页最后一个数据的_id,分页数量] 分页数量默认为10
* @param {String} fields 需要展示的字段 a,b,c,d
* @param {String} unFields 不需要展示的字段 a,b,c,d
* @param {String} querys 查询条件 如 a=1&b=2&c=3
* @param {String} orderBy 排序字段
* @param {String} sort 排序方式【1:正序,-1:倒序】(必须与orderBy同时出现)
* @param {String} dates 时间范围/时间查询 [开始时间,结束时间]/查询时间
*
* @version 1.0.1
* 2018/10/17 修改返回值类型可直接供mongoose使用
*
* @example
* GET /user/?orderBy=compName&sort=-1&pages=20,1&fields=compName,compAuth,compEmail&unFields=status&dates=2018-8-5&name=lopo
* index(){
* const {ctx} = this;
* const {queryParamFn} = ctx.helper;
* ctx.body = queryParamFn(ctx.query);
* }
*
* @return {Object}
* {
* querys: { name: 'lopo' },
* select: { compName: 1, compAuth: 1, compEmail: 1, status: 0 },
* pages: { marker: '1', limit: '20' },
* sort: { compName: '-1' },
* dates: '2018-8-5'
* }
*/
queryParamFn(querys, db = false) {
const mapFN = (str) => (field, val) => str[field].split(',').reduce((a, b) => {
a[b] = val;
return a
}, {});
let strToArr = mapFN(querys);
const sort = !!querys.sort ? JSON.parse(querys.sort) : {};
// const dates = querys.dates || [];
const fields = !!querys.fields ? strToArr('fields', 1) : {};
const unFields = !!querys.unFields ? strToArr('unFields', 0) : {};
const limit = !!querys.limit ? querys.limit * 1 : 10;
const marker = querys.marker || ''
const dateFn = () => {
if (!!dates) {
const ARR = dates.split(',');
return ARR.length === 2 ? {
'$gte': ARR[1],
'$lte': ARR[0]
} : `${ARR}`
}
}
delete querys.pages;
delete querys.fields;
delete querys.unFields;
delete querys.sort;
delete querys.dates;
delete querys.marker;
delete querys.limit;
delete querys._;
delete querys._type;
return {
'querys': {
...querys,
} || {},
'select': {
...fields,
...unFields
},
limit,
marker,
sort,
// ...!!dates && {
// 'dates': dateFn()
// }
}
},
/**
* PD分离
* @param {String} psd PD
* @param {String} pilipala chicken
*
* @return {String} 返回分离后的字符串
*/
bilibole(psd, pilipala) {
let bilibole = crypto.createHash('md5').update(`${psd}:${pilipala}`).digest('hex');
return `${pilipala}${bilibole}`;
},
/**
* @name stream 转buffer
*
* @param {files} stream 文件流
* @param {Function} fn 文件处理函数
*
* @return {String:Buffer} 返回Buferr 可用函数对Buffer进行处理后返回
*/
async stream2buf(stream, fn) {
try {
const parts = await toArray(stream);
const buf = Buffer.concat(parts);
return !!fn ? fn(buf) : buf
} catch (err) {
await sendToWormhole(stream);
throw err;
};
},
/**
* len 随机位数 默认6位
* @param {Number} len 随机数字长度 默认为6
*/
roundCode: (len = 6) => {
let pwd = "";
for (let idx = 0; idx < len; idx++) {
let seed = parseInt(Math.random() * 9);
pwd += seed;
}
return pwd;
},
/**
* 获取token函数
* @param {String} type token类型 ['BAIDUAip','WEWork','WeiXin','DirectAdmin','openstack']
*/
async TokenFn(type) {
const {
ctx,
config
} = this;
let datas;
const DirectAdmin = config.vhost.DirectAdmin;
const wechatApi = config.wechatApi;
const openstackApi = config.openstack;
const tokenMapFn = async () => {
const maps = {
// 百度token
'BAIDUAip': await ctx.curl('aip.baidubce.com/oauth/2.0/token', {
method: 'POST',
dataType: 'json',
data: {
'grant_type': 'client_credentials',
'client_id': config.baiduAIP.API_KEY,
'client_secret': config.baiduAIP.SECRET_KEY
}
}),
// 企业微信token
'WEWork': await ctx.curl('https://qyapi.weixin.qq.com/cgi-bin/gettoken', {
dataType: 'json',
data: {
'corpid': config.weWork.corpid,
'corpsecret': config.weWork.corpsecret
}
}),
// 微信token
'WeiXin': await ctx.curl('https://api.weixin.qq.com/cgi-bin/token', {
dataType: 'json',
data: {
'grant_type': 'client_credential',
'appid': wechatApi.appId,
'secret': wechatApi.appSecret
}
}),
// // DA token
// 'DirectAdmin': await ctx.curl(`${DirectAdmin.server}:${DirectAdmin.port}/CMD_LOGIN`, {
// method: 'POST',
// data: {
// 'username': DirectAdmin.username,
// 'password': DirectAdmin.password
// }
// }),
// openstack token
'openstack': await ctx.curl(`${openstackApi.uri}:5000/v3/auth/tokens?nocatalog`, {
method: 'POST',
dataType: 'json',
headers: {
'Content-Type': 'application/json',
},
content: `{
"auth": {
"identity": {
"methods": ["password"],
"password": {
"user": {
"name": "${openstackApi.name}",
"password": "${openstackApi.password}",
"domain": { "name": "Default" }
}
}
},
"scope": {
"project": {
"domain": {
"id": "default"
},
"name": "admin"
}
}
}
}`,
})
};
return maps[type]
};
const setResult = async () => {
if (type !== 'DirectAdmin') {
const data = await tokenMapFn();
return {
tokenValue: type === 'openstack' ? data.headers['x-subject-token'] : data.data.access_token,
tokenType: type,
tokenExpiresIn: type === 'openstack' ? new Date(data.data.token.expires_at) - 0 - 60 * 1000 : data.data.expires_in * 1000 + (new Date() - 0)
}
} else {
const cookies = (await tokenMapFn()).res.headers['set-cookie'][0].split(';');
return {
tokenValue: cookies[0],
tokenType: 'DirectAdmin',
tokenExpiresIn: (new Date(cookies[2].split('=')[1]) - 0) + 8 * 60 * 60 * 1000
}
}
};
const setToken = async () => {
const saveToken = await ctx.model.Tokens.findOneAndUpdate({
'tokenType': type
}, { ...(await setResult())
}, {
upsert: true,
new: true
});
datas = saveToken.tokenValue;
};
const tokens = await ctx.model.Tokens.findOne({
'tokenType': type
});
!!tokens && tokens.tokenExpiresIn > (new Date - 0) ? datas = tokens.tokenValue : await setToken();
return {
data: datas
}
},
/**
* mongo报错处理
*/
mongoError(error) {
const errorMap = {
'11000': ''
}
},
/**
* 函数柯里化
*/
curry: (fn, arity = fn.length, ...args) => arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args),
/**
* moment mix
* @param {String} scope [/day/week/month/quarter/year] 日/周/月/季度/年
* @int {Number} int 数值
*/
// 当前时间
getThisMoment: (scope) => {
return {
startDate: moment().startOf(scope).format(),
endDate: moment().format()
}
},
// 之前
getPrevMoment: (scope) => {
return {
startDate: moment().week(moment()[scope]() - 1).startOf(scope).format(),
endDate: moment().week(moment()[scope]() - 1).endOf(scope).format()
}
},
// 时间差 type [add,subtract]
subtractMoment: (int, scope, type = "subtract", withFormat = true, fn) => {
const dates = moment()[type](int, scope);
return !fn ? (withFormat ? dates.format() : dates) : fn(dates);
},
/**
* @name 格式化时间
* @param {String} formater 格式化输出标准 YYYY/MM/DD/HH/mm/ss/SSS
* @param {Date} dates 日期
* @param {Boolean} utc UTC
*/

formatDate: ({
formater,
dates
} = {}, utc = false) => {
const DATE = moment(!!dates ? dates : new Date());
return utc ? DATE.utc().format(!!formater && formater) : DATE.format(!!formater && formater)
},
// 生成时间数组
getDayAll: (begin, end) => {
let dateAllArr = new Array();
let ab = moment(begin).format("YYYY-MM-DD").split("-");
const ae = moment(end).format("YYYY-MM-DD").split("-");
const db = new Date();
db.setUTCFullYear(ab[0], ab[1] - 1, ab[2]);
const de = new Date();
de.setUTCFullYear(ae[0], ae[1] - 1, ae[2]);
const unixDb = db.getTime();
const unixDe = de.getTime();
for (let k = unixDb; k <= unixDe;) {
dateAllArr.push(new Date(parseInt(k)).format().toString());
k = k + 24 * 60 * 60 * 1000;
}
return dateAllArr;
},
getCTX: () => app.createAnonymousContext(),
// 发送短信,邮件,企业微信通知
async sendWarning(ctx, content, _id, compName) {
const auth = ctx.state.user;
const {
compEmail,
compPhone
} = await ctx.model.Company.findOne({
'_id': _id
}, {
'compEmail': 1,
'compPhone': 1
});
// await ctx.service.wework.message.send({ totag: 2, content: content.replace(/您/g, `${compName}`) });
await ctx.service.sms.svipwang.create(compPhone, content);
await ctx.service.sms.mail.sendMailDefault({
subject: '系统消息通知',
to: compEmail,
html: content
});
},
// 清理paramsFN()
deleteUndefined: (object) => {
for (let key in object) {
key !== 'detail' && delete object[(object[key] === undefined || object[key] === '' || object[key] === '[]') && key]
}
return object
},
/******************************************************************************************/
/**
* class DistributedEdit extends mix(class1, class2)
*/
// mixins
mix: (...mixins) => {
class Mix {}

for (let mixin of mixins) {
copyProperties(Mix.prototype, mixin); // 拷贝实例属性
copyProperties(Mix.prototype, Reflect.getPrototypeOf(mixin)); // 拷贝原型属性
}

return Mix;
},
// mixinsParams
copyProperties: (target, source) => {
for (let key of Reflect.ownKeys(source)) {
if (key !== "constructor" &&
key !== "prototype" &&
key !== "name"
) {
let desc = Object.getOwnPropertyDescriptor(source, key);
Object.defineProperty(target, key, desc);
}
}
},
/***************end****************/
/**
* 货币计算 mix
* @param {String} method [add/+,sub/-,mul/*,div//] 加减乘除
* @param {Array} money 需要计算的数字【前,后】
*/
decimalCash: (method = "add", money) => {
return new Decimal(money[0])[method](new Decimal(money[1])).toNumber()
},
// decimaFN: () => { return this.decimalCash() },
// decimalAdd: () => { return this.decimaFN('add') },
// decimalSub: () => { return this.decimaFN('sub') },
// decimalMul: () => { return this.decimaFN('mul') },
// decimalDiv: () => { return this.decimaFN('div') },
/****************end***************/
/**
* MONGO type转换
* @param {Array} ARR 字符串字典
* @param {Number} value 值
* @param {Boolean} hasZero 是否从0开始
*/
NumToStr(ARR, value, hasZero = false) {
return `${value}|${ARR[hasZero ? value : value - 1]}`
},
/**
* @name mongoDB update数组参数
*
*/
objToQuery(OBJ, KEY) {
return Object.keys(OBJ).reduce((a, b) => {
a[`${KEY}.$.${b}`] = OBJ[b];
return a
}, {});
},
/**
* @name 线转树
* @param {Array} ARR 需要处理的数组
* @param {String} keyName 作为分类的字段
*/
Array2Object: (ARR, keyName) => {
return ARR.reduce((a, b) => {
const keys = b[keyName];
if (a[keys] === undefined) a[keys] = [];
a[keys].push(b);
return a;
}, {})
},
/**
* @name 字典排序
*/
OBJKeySort: (obj) => {
const newkey = Object.keys(obj).sort();
const newObj = {};
for (var i = 0; i < newkey.length; i++) {
newObj[newkey[i]] = obj[newkey[i]];
}
return newObj;
},
/**
* @name 字母升序
*
* @param {String} chars 字母
* @param {Number} num 进几位
*
*/
nextChars: (chars, num, type = 'lower') => {
const char = chars.toLowerCase();
const isChar = /^[a-zA-Z]*$/.test(char);
const cx = char.charCodeAt(0);
const CHARS = (!!isChar && cx + num < 123) ? String.fromCharCode(char.charCodeAt(0) + num) : false;
return !!CHARS ? type === 'upper' ? CHARS.toUpperCase() : CHARS : 'Params Error'
},
/**
* @name 扁平化数组 fr : _.Array
*
* @param {Array} arr 数组
*
*/
deepFlatten: arr => _Array.flatten(arr),
/**
* @name 集合排序 fr : _.Collection
*
* @param {String|Function|Array|Object} 排序字段
* @param {String} asc|desc 排序方式
*
*/
orderBy: (arr, iteratees, order = 'asc') => _Collection.orderBy(arr, iteratees, order),
/**
* @name 隐藏手机号中间4位
*
*@param {String} telphone 手机号码
*/
hidePhone: (telphone) => {
const reg = /^(\d{3})\d{4}(\d{4})$/;
return telphone.replace(reg, `$1****$2`)
}
};

openstack gnocchi 服务器硬件监控 (基于Eggjs实现)

openstacklopo1983 发表了文章 • 0 个评论 • 433 次浏览 • 2018-12-22 02:51 • 来自相关话题

'use strict';

const ServerIndex = require('../index')
/**
* @name 获取监控数据
* @param {*} _ucid 用户id
* @param {*} server_id 服务器id
* @param {*} type 聚合类型
* @param {*} metric 聚合数据
* @description type和metric 子属关系如下
*
* instance?["vcpus", "memory", "disk.root.size", "compute.instance.booting.time", "cpu_util", "disk.ephemeral.size", "cpu.delta", "cpu", "memory.usage"]
*
* instance_network_interface ["network.outgoing.packets.rate", "network.incoming.bytes.rate", "network.outgoing.bytes.rate", "network.incoming.packets", "network.incoming.packets.rate", "network.outgoing.bytes", "network.incoming.bytes", "network.outgoing.packets"]
*
* instance_disk ["disk.device.read.bytes.rate", "disk.device.write.requests", "disk.device.write.bytes.rate", "disk.device.write.requests.rate", "disk.device.read.bytes", "disk.device.read.requests", "disk.device.read.requests.rate", "disk.device.write.bytes"]
*
*/
class ResourceService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.actions = ':8041/v1/';
}
async getMeasures(_ucid, { server_id, type, metric } = {}) {
const ctx = this.ctx;
const { subtractMoment } = ctx.helper;
try {
const metricIDs = await this.getmetricID(_ucid, server_id, type, metric);
const getDATASFn = metricIDs.map(async e => {
return {
name: e.name,
datas: await this.OSAJax(`${this.actions}metric/${e.id}/measures`, {
body: { start: subtractMoment(1, 'hour') }
})
}
})
const datas = (await Promise.all(getDATASFn)).reduce((a, b) => {
a[`${b['name']}`] = b['datas'];
return a
}, {})
return { data: datas };
} catch (error) {
console.log(error)
return error
}
}
async getmetricID(_ucid, server_id, type, metric) {
const metrics = metric.split(',')
try {
const DATAS = await this.OSAJax(`${this.actions}search/resource/generic/`, {
_ucid,
method: 'POST',
body: {
"and": [
{
"like": {
"original_resource_id": `%${server_id}%`
}
},
{
"=": {
"type": type
}
}
]
}
});
const { code } = DATAS;
if (!code && !!DATAS.length) {
const MAPS = DATAS[0].metrics
return metrics.reduce((a, b) => { a.push({ name: b, id: MAPS[b] }); return a }, )
} else {
return undefined
}
} catch (error) {
return error
}
}
}

module.exports = ResourceService;[/b]?'use strict';
const Service = require('egg').Service;
//
class indexService extends Service {
constructor(ctx) {
super(ctx);
this.OPTokenMODEL = ctx.model.Openstack.Token;
this.OPUserMODEL = ctx.model.Openstack.Identity.User;
this.OBJKeySort = ctx.helper.OBJKeySort;
const { uri } = this.config.openstack;
this.uri = `${uri}`;
this.CurlOpt = async (method, full, token) => {
return {
'dataType': 'json',
'headers': {
...((!full && !token) && { 'X-Auth-Token': (await ctx.helper.TokenFn('openstack')).data }) || !!token && { 'X-Auth-Token': token },
'Content-Type': 'application/json',
},
'method': method,
...method !== 'DELETE' && { 'data': {} },
'timeout': 60000
}
}
};
/**
* 基础库
*
* @param {Object} body 需要提交的参数
* @param {String} method GET POST DELETE PUT PATCH
* @param {String} params url 参数
* @param {Boolean} full 是否显示完整参数
* @param {String} _ucid 用户ID Optional
*/
async OSAJax(params, { body = {}, method = "GET", full = false, status = false, _ucid } = {}) {
const ctx = this.ctx;
const opt = await this.CurlOpt(method, full, !!_ucid && await this.getUserToken(_ucid));
method !== 'DELETE' && Object.assign(opt.data, { ...!!body ? body : '' });
try {
const result = await ctx.curl(`${this.uri}${!!params ? params : ''}`, opt);
return !!full && result || !!status && result.status || result.data;
} catch (error) {
return error
}
}
}
module.exports = indexService; 查看全部
gnocchi-logo.png
'use strict';

const ServerIndex = require('../index')
/**
* @name 获取监控数据
* @param {*} _ucid 用户id
* @param {*} server_id 服务器id
* @param {*} type 聚合类型
* @param {*} metric 聚合数据
* @description type和metric 子属关系如下
*
* instance?["vcpus", "memory", "disk.root.size", "compute.instance.booting.time", "cpu_util", "disk.ephemeral.size", "cpu.delta", "cpu", "memory.usage"]
*
* instance_network_interface ["network.outgoing.packets.rate", "network.incoming.bytes.rate", "network.outgoing.bytes.rate", "network.incoming.packets", "network.incoming.packets.rate", "network.outgoing.bytes", "network.incoming.bytes", "network.outgoing.packets"]
*
* instance_disk ["disk.device.read.bytes.rate", "disk.device.write.requests", "disk.device.write.bytes.rate", "disk.device.write.requests.rate", "disk.device.read.bytes", "disk.device.read.requests", "disk.device.read.requests.rate", "disk.device.write.bytes"]
*
*/
class ResourceService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.actions = ':8041/v1/';
}
async getMeasures(_ucid, { server_id, type, metric } = {}) {
const ctx = this.ctx;
const { subtractMoment } = ctx.helper;
try {
const metricIDs = await this.getmetricID(_ucid, server_id, type, metric);
const getDATASFn = metricIDs.map(async e => {
return {
name: e.name,
datas: await this.OSAJax(`${this.actions}metric/${e.id}/measures`, {
body: { start: subtractMoment(1, 'hour') }
})
}
})
const datas = (await Promise.all(getDATASFn)).reduce((a, b) => {
a[`${b['name']}`] = b['datas'];
return a
}, {})
return { data: datas };
} catch (error) {
console.log(error)
return error
}
}
async getmetricID(_ucid, server_id, type, metric) {
const metrics = metric.split(',')
try {
const DATAS = await this.OSAJax(`${this.actions}search/resource/generic/`, {
_ucid,
method: 'POST',
body: {
"and": [
{
"like": {
"original_resource_id": `%${server_id}%`
}
},
{
"=": {
"type": type
}
}
]
}
});
const { code } = DATAS;
if (!code && !!DATAS.length) {
const MAPS = DATAS[0].metrics
return metrics.reduce((a, b) => { a.push({ name: b, id: MAPS[b] }); return a }, )
} else {
return undefined
}
} catch (error) {
return error
}
}
}

module.exports = ResourceService;[/b]
?
'use strict';
const Service = require('egg').Service;
//
class indexService extends Service {
constructor(ctx) {
super(ctx);
this.OPTokenMODEL = ctx.model.Openstack.Token;
this.OPUserMODEL = ctx.model.Openstack.Identity.User;
this.OBJKeySort = ctx.helper.OBJKeySort;
const { uri } = this.config.openstack;
this.uri = `${uri}`;
this.CurlOpt = async (method, full, token) => {
return {
'dataType': 'json',
'headers': {
...((!full && !token) && { 'X-Auth-Token': (await ctx.helper.TokenFn('openstack')).data }) || !!token && { 'X-Auth-Token': token },
'Content-Type': 'application/json',
},
'method': method,
...method !== 'DELETE' && { 'data': {} },
'timeout': 60000
}
}
};
/**
* 基础库
*
* @param {Object} body 需要提交的参数
* @param {String} method GET POST DELETE PUT PATCH
* @param {String} params url 参数
* @param {Boolean} full 是否显示完整参数
* @param {String} _ucid 用户ID Optional
*/
async OSAJax(params, { body = {}, method = "GET", full = false, status = false, _ucid } = {}) {
const ctx = this.ctx;
const opt = await this.CurlOpt(method, full, !!_ucid && await this.getUserToken(_ucid));
method !== 'DELETE' && Object.assign(opt.data, { ...!!body ? body : '' });
try {
const result = await ctx.curl(`${this.uri}${!!params ? params : ''}`, opt);
return !!full && result || !!status && result.status || result.data;
} catch (error) {
return error
}
}
}
module.exports = indexService;

queries 分离函数 适用 mongoose

Nodejslopo1983 发表了文章 • 0 个评论 • 318 次浏览 • 2018-10-17 21:34 • 来自相关话题

? // app/extend/helper.js
/**
* GET querys 分离函数
* @param {String} pages 分页 [上一页最后一个数据的_id,分页数量] 分页数量默认为10
* @param {String} fields 需要展示的字段 a,b,c,d
* @param {String} unFields 不需要展示的字段 a,b,c,d
* @param {String} querys 查询条件 如 a=1&b=2&c=3
* @param {String} orderBy 排序字段
* @param {String} sort 排序方式【1:正序,-1:倒序】(必须与prderBy同时出现)
* @param {String} dates 时间范围/时间查询 [开始时间,结束时间]/查询时间
*
* @version 1.0.1
* 2018/10/17 修改返回值类型可直接供mongoose使用
*
* @example
* GET /user/?orderBy=compName&sort=-1&pages=20,1&fields=compName,compAuth,compEmail&unFields=status&dates=2018-8-5&name=lopo
* @return {Object}
* {
* querys: { name: 'lopo' },
* select: { compName: 1, compAuth: 1, compEmail: 1, status: 0 },
* pages: { marker: '1', limit: '20' },
* sort: { compName: '-1' },
* dates: '2018-8-5'
* }
*/
queryParamFn(querys) {
const mapFN = (str) => (field, val) => str[field].split(',').reduce((a, b) => { a<strong> = val; return a }, {});
let strToArr = mapFN(querys);
const pages = !!querys.pages && querys.pages;
const orderBy = !!querys.orderBy && querys.orderBy;
const sort = !!querys.sort && querys.sort;
const dates = !!querys.dates && querys.dates;
const fields = !!querys.fields ? strToArr('fields', 1) : {};
const unFields = !!querys.unFields ? strToArr('unFields', 0) : {};
const dateFn = () => {
if (!!dates) {
const ARR = dates.split(',');
return ARR.length === 2 ? { '$gte': ARR[1], '$lte': ARR[0] } : `${ARR}`
}
}
const sortFn = () => {
let OBJ = {};
if (!!orderBy) {
OBJ[orderBy] = sort;
return OBJ
}
return OBJ || {}
}
delete querys.pages;
delete querys.fields;
delete querys.unFields;
delete querys.orderBy;
delete querys.sort;
delete querys.dates;
return {
'querys': querys || {},
'select': { ...fields, ...unFields },
...(!!pages && {
'pages': {
'marker': pages.split(',')[1] || undefined,
'limit': pages.split(',')[0] || 10
}
}),
...!!orderBy && {
'sort': sortFn()
},
...!!dates && {
'dates': dateFn()
}
}
} 查看全部
?
    // app/extend/helper.js
/**
* GET querys 分离函数
* @param {String} pages 分页 [上一页最后一个数据的_id,分页数量] 分页数量默认为10
* @param {String} fields 需要展示的字段 a,b,c,d
* @param {String} unFields 不需要展示的字段 a,b,c,d
* @param {String} querys 查询条件 如 a=1&b=2&c=3
* @param {String} orderBy 排序字段
* @param {String} sort 排序方式【1:正序,-1:倒序】(必须与prderBy同时出现)
* @param {String} dates 时间范围/时间查询 [开始时间,结束时间]/查询时间
*
* @version 1.0.1
* 2018/10/17 修改返回值类型可直接供mongoose使用
*
* @example
* GET /user/?orderBy=compName&sort=-1&pages=20,1&fields=compName,compAuth,compEmail&unFields=status&dates=2018-8-5&name=lopo
* @return {Object}
* {
* querys: { name: 'lopo' },
* select: { compName: 1, compAuth: 1, compEmail: 1, status: 0 },
* pages: { marker: '1', limit: '20' },
* sort: { compName: '-1' },
* dates: '2018-8-5'
* }
*/
queryParamFn(querys) {
const mapFN = (str) => (field, val) => str[field].split(',').reduce((a, b) => { a<strong> = val; return a }, {});
let strToArr = mapFN(querys);
const pages = !!querys.pages && querys.pages;
const orderBy = !!querys.orderBy && querys.orderBy;
const sort = !!querys.sort && querys.sort;
const dates = !!querys.dates && querys.dates;
const fields = !!querys.fields ? strToArr('fields', 1) : {};
const unFields = !!querys.unFields ? strToArr('unFields', 0) : {};
const dateFn = () => {
if (!!dates) {
const ARR = dates.split(',');
return ARR.length === 2 ? { '$gte': ARR[1], '$lte': ARR[0] } : `${ARR}`
}
}
const sortFn = () => {
let OBJ = {};
if (!!orderBy) {
OBJ[orderBy] = sort;
return OBJ
}
return OBJ || {}
}
delete querys.pages;
delete querys.fields;
delete querys.unFields;
delete querys.orderBy;
delete querys.sort;
delete querys.dates;
return {
'querys': querys || {},
'select': { ...fields, ...unFields },
...(!!pages && {
'pages': {
'marker': pages.split(',')[1] || undefined,
'limit': pages.split(',')[0] || 10
}
}),
...!!orderBy && {
'sort': sortFn()
},
...!!dates && {
'dates': dateFn()
}
}
}