javascript

javascript

egg 处理提交的xml

Nodejslopo1983 发表了文章 • 0 个评论 • 2379 次浏览 • 2019-09-04 17:09 • 来自相关话题

最近在做企业微信相关的接口转发 发现回调是XML 数据格式 
egg默认是没有处理xml(目前在文档里面没找到.....)
看了下egg的源码 发现处理request 是用的bodyParser

config.default.js config.bodyParser = {
enable: true,
// @see https://github.com/hapijs/qs/b ... %23L8 for more options
queryString: {
arrayLimit: 100,
depth: 5,
parameterLimit: 1000,
},
enableTypes: ['json', 'form', 'text'],
extendTypes: {
text: ['text/xml', 'application/xml'],
},
};
同样的方法可以处理其他的任何格式 查看全部
最近在做企业微信相关的接口转发 发现回调是XML 数据格式 
egg默认是没有处理xml(目前在文档里面没找到.....)
看了下egg的源码 发现处理request 是用的bodyParser

config.default.js
    config.bodyParser = {
enable: true,
// @see https://github.com/hapijs/qs/b ... %23L8 for more options
queryString: {
arrayLimit: 100,
depth: 5,
parameterLimit: 1000,
},
enableTypes: ['json', 'form', 'text'],
extendTypes: {
text: ['text/xml', 'application/xml'],
},
};

同样的方法可以处理其他的任何格式


js偏函数

javascript/jQuerylopo1983 发表了文章 • 0 个评论 • 1604 次浏览 • 2019-06-10 09:50 • 来自相关话题

有一种函数叫偏函数( 左倾 ),其原理是将一些函数组合封装到一个函数中,调用时可以按顺序实现全部功能。使用时大多需要配合reduceRight
const compose = (...args) => x => args.reduceRight((result, cb) => cb(res), x); 查看全部

有一种函数叫偏函数( 左倾 ),其原理是将一些函数组合封装到一个函数中,调用时可以按顺序实现全部功能。使用时大多需要配合reduceRight


const compose = (...args) => x => args.reduceRight((result, cb) => cb(res), x);

egg.js+mongodb+openstack 公有云计费系统(三)OpenStack 对接 blcokStorage(2)

Nodejslopo1983 发表了文章 • 0 个评论 • 1386 次浏览 • 2019-03-15 11:27 • 来自相关话题

磁盘相关
控制器'use strict';

const Controller = require('egg').Controller;

class OPBlcokStorageVolumeController extends Controller {
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.QMFN = ctx.helper.queryParamFn;
this.DUFN = ctx.helper.deleteUndefined;
this.volumeMODEL = ctx.model.Openstack.BlcokStorage.Volumes;
this.volumeSERVICE = ctx.service.openstack.blcokStorage.volume;
};
/**
* @name 磁盘列表
*
* @param {String} detail 不需要参数
*
* ----------------------------------- 分页 -------------------------------------
*
* @param {String} limit 分页>>条数/页
* @param {String} marker markerID(上页最后条id)
*
* ----------------------------------- 排序 -------------------------------------
*
* @param {String} sort_dir a|b 排序方式 ['asc':default,'desc']
* @param {String} sort_key 排序字段 [status,name,created_at]
*
* @description query参数detail存在则显示实力配置列表详细信息,若不传仅显示概要列表
*
* @example GET /openstack/blcokstorage/volume?{detail}
*
*/
async index() {
const { ctx } = this;
const RESULT = await this.volumeMODEL.getUsers(this.auth.id, this.QMFN({...ctx.query }));
ctx.body = RESULT;
};
/**
* @name 获取单一磁盘
*
* @description
*
* @example GET /openstack/blcokstorage/volume/{id}
*
*/
async show() {
const ctx = this.ctx;
// const datas = await this.volumeSERVICE.show(ctx.params.id, this.auth.id);
// ctx.body = datas;
const RESULT = await this.volumeMODEL.getOne(this.auth.id, ctx.params.id, ctx.isAdmin())
ctx.body = RESULT;
};
/**
* @name 创建磁盘
*
* @description
*
* @example POST /openstack/blcokstorage/volume
*
* @param {String} size 大小
* @param {String} availability_zone Optional 可用区域
* @param {String} snapshot_id Optional 快照ID FN从快照创建
* @param {String} backup_id Optional 备份ID FN从备份创建
* @param {String} imageRef Optional 镜像ID FN从镜像创建
* @param {String} volume_type Optional 磁盘类型 默认HDD
*
*/
async create() {
const { ctx } = this;
const { availability_zone, snapshot_id, backup_id, imageRef, size = 5, volume_type = "hdd" } = ctx.request.body;
const BODYS = this.DUFN({ availability_zone, snapshot_id, backup_id, imageRef, size, volume_type })
const datas = await this.volumeMODEL.createOne(this.auth.id, BODYS)
ctx.body = datas
};
/**
* @name 调整磁盘
*
* @description
*
* @example POST /openstack/blcokstorage/volume
*
* @param {String} size 大小
* @param {String} availability_zone Optional 可用区域
* @param {String} snapshot_id Optional 快照ID FN从快照创建
* @param {String} backup_id Optional 备份ID FN从备份创建
* @param {String} imageRef Optional 镜像ID FN从镜像创建
* @param {String} volume_type Optional 磁盘类型 默认HDD
*
*/
async update() {
const ctx = this.ctx;
const BODYS = ctx.request.body;
const { action, name } = BODYS;
delete BODYS.action;
const datas = !!action ? await this.volumeSERVICE.action(this.auth.id, this.auth.ProjectID, ctx.params.id, action, BODYS) : await this.serverSERVICE.update(ctx.params.id, this.auth.id, this.DUFn({ name }));
ctx.body = datas
};
/**
* @name 删除磁盘
*
* @description
*
*/
async destroy() {
const ctx = this.ctx;
const DATAS = await this.volumeSERVICE.destroy(this.auth.id, this.auth.ProjectID, ctx.params.id);
ctx.body = DATAS
}

}

module.exports = OPBlcokStorageVolumeController;modelmodule.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const ctx = app.createAnonymousContext();
const {
helper,
service
} = app.createAnonymousContext();
const {
NumToStr
} = ctx.helper;
const OSblockStorageVolumeSchema = new Schema({
// 配置
config: {},
// 公司id
_comp: {
type: Schema.Types.ObjectId,
ref: 'Company',
required: true,
index: true
},
// 支付方式
payment: {
type: String,
default: 'prepay'
},
// 到期时间
endTime: {
type: Date
},
// 后台备注描述
_description: {

},
// 源信息
_metadata: {
type: String,
index: true
},
/*---------------------------------------------------*/
"status": {
type: 'String',
default: 'creating'
},
"migration_status": {},
"user_id": {},
"attachments": ,
"availability_zone": {},
"bootable": {},
"encrypted": {},
"created_at": {},
"description": {},
"updated_at": {},
"volume_type": {},
"name": {},
"replication_status": {},
"consistencygroup_id": {},
"source_volid": {},
"imageRef": {},
"backup_id": {},
"snapshot_id": {},
"multiattach": {},
"metadata": {},
"id": {
type: String,
index: true
},
"size": {},
"os-vol-host-attr:host": {},
"os-vol-tenant-attr:tenant_id": {}
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
OSblockStorageVolumeSchema.statics = {
getUsers: async function(_comp, queryMix) {
const serverSERVICE = ctx.service.openstack.server.servers;
const {
querys,
select,
sort,
limit,
marker
} = queryMix;
const withDetail = querys.detail === '' || querys.detail;
delete querys.detail;
let QUERTS = {};
for (const key in querys) {
QUERTS[key] = eval(`/${querys[key]}.*/i`)
}
const MODELS = (count = false) => this.find({
_comp,
...QUERTS,
...(marker && !count && {
'_id': {
"$lt": marker
}
}),
}, {
...select,
}, {
...!count && {
limit
},
'sort': {
_id: -1,
...sort
}
});
try {
const datas = await MODELS()
return {
data: {
result: datas,
limit,
marker: datas.length ? [...datas].pop()._id : '',
totalCount: await MODELS(true).count()
}
}
} catch (error) {
return error
}
},
/**
* @name 创建硬盘
* @param {*} _id
* @param {*} body
*/
createOne: async function(_id, bodys) {
try {
const {
endTime,
payment,
_metadata
} = bodys;
delete bodys._metadata;
delete bodys.endTime;
delete bodys.payment;
//
const DATAS = await service.openstack.blcokStorage.volume.create(_id, {
...bodys,
... {
"name": `volume-${Math.random().toString(36).slice(2, 8)}`
}
});
const {
id: _openstack_id
} = DATAS.data.volume;
!!DATAS.data.volume && await ctx.model.Schedule.Openstack.Volume.addOne({
_comp: _id,
_openstack_id,
type: 'volume',
active: 'creating',
_metadata
})
return !!DATAS.data.volume ? {
data: await this.create({
_comp: _id,
endTime,
payment,
_metadata,
...DATAS.data.volume
})
} : {
'code': 422,
'message': '创建失败'
};
} catch (error) {
return error
}
},
// 获取全部
getAll: async function(queryMix) {
const {
querys,
select,
pages,
sort,
dates
} = queryMix;
const MODELS = this.find({
'status': {
'$ne': 0
},
...querys,
// ...(!!pages && !!pages.marker && { '_id': { "$lt": pages.marker } }),
}, {
...select,
})
// .limit(!!pages ? pages.limit : 10)
.sort({
// '_id': -1,
'id': 1,
...sort
});
return {
data: {
result: await MODELS,
totalCount: await MODELS.count()
}
};
},
// 查看单个
getOne: async function(_comp, id, isAdmin) {
const MODELS = this.findOne({...isAdmin ? {
id
} : {
_comp,
id
}
});
try {
const RESULT = await MODELS;
return {
data: RESULT
}
} catch (error) {
return error
}
},
// 绑定到磁盘
/**
* @name 绑定到磁盘
* @param {*} _comp 用户UUID
* @param {*} id 磁盘ID
* @param {*} type 'bind'|'unbind' 绑定方式
*/
bindToECS: async function({
_comp,
id,
type = "bind"
} = {}, bodys) {
const MODEL = this.findOneAndUpdate({
_comp,
id
}, {
'attachments': type === 'bind' ? [bodys] : ,
'status': type === 'bind' ? 'in-use' : 'available'
});
try {
const datas = await MODEL;
} catch (error) {
return error
}
}
}
return mongoose.model('openstack_block_storage_volume', OSblockStorageVolumeSchema)
}service'use strict';
const ServerIndex = require('../index')
//
class VolumeService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.adminprojectID = this.config.openstack.projectID.default;
this.actions = ':8776/v3';
};
// 获取磁盘列表
async list(querys, _ucid, project_id, isAdmin) {
try {
const detail = Object.keys(querys).includes('detail');
delete querys.detail;
let SORT = {}
if (querys.sort_key) {
SORT = { 'sort': `${querys.sort_key}:${querys.sort_dir}` };
delete querys.sort_key;
delete querys.sort_dir;
}
const datas = await this.OSAJax(`${this.actions}/${!!isAdmin ? this.adminprojectID : project_id}/volumes${detail ? '/detail' : ''}`, {
...!isAdmin && { _ucid },
body: {
...querys,
...querys.sort_key && SORT,
...isAdmin && { 'all_tenants': true },
},
});
return {
data: {
result: datas.volumes.map(e => {
!!detail && e.attachments.length && (e.attachment = e.attachments[0].server_id)
delete e.links;
delete e.attachments;
return e
}).filter(e => e.status != 'deleting'),
totalCount: datas.volumes.length
}
};
} catch (error) {
return error
}
}
// 获取磁盘详情
async show(id, _ucid, _description) {
try {
const datas = await this.OSAJax(`${this.actions}/${await this.getProject(_ucid)}/volumes/${id}`, {
..._ucid !== 'admin' && { _ucid }
});
return _ucid === 'admin' ? { ...datas.volume, _description } : { data: datas.volume };
} catch (error) {
return error
}
}
// 创建磁盘
async create(_ucid, bodys, order = "false") {
const DATAS = this.OSAJax(`${this.actions}/${await this.getProject(_ucid)}/volumes`, {
body: {
'volume': {
... {
"availability_zone": null,
"source_volid": null,
"description": null,
"multiattach": false,
"snapshot_id": null,
"backup_id": null,
"imageRef": null,
"metadata": {},
"consistencygroup_id": null
},
...bodys
}
},
method: 'POST',
_ucid
});
try {
const datas = await DATAS
return !!order ? { data: datas } : datas
} catch (error) {
return error
}

}
// 删除磁盘
async destroy(_ucid, project_id, volume_id) {
try {
const datas = await this.OSAJax(`${this.actions}/${project_id}/volumes/${volume_id}`, {
_ucid,
method: 'DELETE',
full: true
});
return {
...datas.status === 404 && {
message: `${volume_id}不存在或已删除`
}
}
} catch (error) {

}
}
// 磁盘操作
// {'os-extend':'磁盘扩容','revert':'镜像回滚'}
async action(_ucid, project_id, volume_id, type, bodys) {
const isIn = (e) => ['os-extend', 'revert'].includes(e);
if (!isIn(type)) return { code: 422 };
const actionMAP = (e) => {
const OBJ = {};
OBJ[e] = !bodys ? null : bodys;
return OBJ;
}
try {
const DATAS = await this.OSAJax(`${this.actions}/${project_id}/volumes/${volume_id}/action`, {
body: actionMAP(type),
method: 'POST',
_ucid, full: true
});
return {
code: DATAS.status
}
} catch (error) {
return error
}
}
async update(_ucid, project_id, volume_id, bodys) {
try {
const DATAS = await this.OSAJax(`${this.actions}/${project_id}/volumes/${volume_id}`, {
_ucid,
bodys: {
...bodys
},
method: 'PUT'
});
return {
data: DATAS
}
} catch (error) {
return error
}
}
}
module.exports = VolumeService;
scheduleconst Subscription = require('egg').Subscription;

class getVolumeStatus extends Subscription {
constructor(ctx) {
super(ctx);
this.SERVICE = ctx.service.openstack.blcokStorage.volume;
this.MODELS = ctx.model.Schedule.Openstack.Volume;
this.volumeMODEL = ctx.model.Openstack.BlcokStorage.Volumes;
}
static get schedule() {
return {
interval: '10s',
type: 'worker',
};
}
async subscribe() {
const ctx = this.ctx;
const lists = await this.MODELS.getALL();
if (lists.length) {
const datas = await this.checkItem(lists);
}
}
async checkItem(result) {
// 调取记录
const checkFN = result.map(e => {
// 记录尝试次数
this.MODELS.retry(e._openstack_id)
return this.SERVICE.show(e._openstack_id, 'admin', e);
})
let DATAS = await Promise.all(checkFN);
// 检查ACTION
if (!!DATAS.length) {
const endOrder = DATAS.map(e => {
const { _comp, _openstack_id: id } = e._description;
delete e._description
delete e.links
if (e.status === 'available' || e.status === 'in-use') {
return this.volumeMODEL.findOneAndUpdate({ id }, { ...e });
}
})
DATAS = await Promise.all(endOrder);
};
// 清除已完成任务
if (DATAS.length) {
const clearSchedule = DATAS.map(e => {
if (!!e) {
const { id: _openstack_id } = e;
return this.MODELS.deleteOne({ '_openstack_id': _openstack_id })
}
})
DATAS = await Promise.all(clearSchedule);
}
}
}

module.exports = getVolumeStatus;
schedule model
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const ctx = app.createAnonymousContext();
const scheduleOSVolumeSchema = new Schema({
// 类型
type: {
type: String,
},
// 尝试次数
retry: {
type: Number,
dafault: 0
},
// 是否创建成功
status: {
type: Boolean,
default: false
},
// 操作方式 [BUILD:创建,DELETE:删除]
active: {
type: String,
},
// 数据ID
_openstack_id: {
type: String,
index: true
},
// 公司ID
_comp: {
type: String
},
// 其他配置参数
/**
* ecs:Number
*/
_description: {

},
// 其他配置元信息
_metadata: {

}
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
scheduleOSVolumeSchema.statics = {
getALL: async function (limit = 50) {
const MODEL = this.find().limit(limit);
try {
return await MODEL;
} catch (error) {
return error
}
},
addOne: async function (bodys) {
const MODEL = this.create({ ...bodys });
try {
const result = await MODEL;
return { code: !!result ? 201 : 404 }
} catch (error) {
return error
}
},
// destroyOne: async function (id) {
// const MODEL = this.deleteOne(id);
// try {
// const result = await MODEL
// return result
// } catch (error) {
// return error
// }
// },
retry: async function (_openstack_id) {
const MODEL = this.findOneAndUpdate({ _openstack_id }, {
'$inc': { retry: 1 }
});
try {
await MODEL
} catch (error) {
return error
}
}
}
return mongoose.model('schedule_openstack_volume', scheduleOSVolumeSchema);
} 查看全部
磁盘相关
控制器
'use strict';

const Controller = require('egg').Controller;

class OPBlcokStorageVolumeController extends Controller {
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.QMFN = ctx.helper.queryParamFn;
this.DUFN = ctx.helper.deleteUndefined;
this.volumeMODEL = ctx.model.Openstack.BlcokStorage.Volumes;
this.volumeSERVICE = ctx.service.openstack.blcokStorage.volume;
};
/**
* @name 磁盘列表
*
* @param {String} detail 不需要参数
*
* ----------------------------------- 分页 -------------------------------------
*
* @param {String} limit 分页>>条数/页
* @param {String} marker markerID(上页最后条id)
*
* ----------------------------------- 排序 -------------------------------------
*
* @param {String} sort_dir a|b 排序方式 ['asc':default,'desc']
* @param {String} sort_key 排序字段 [status,name,created_at]
*
* @description query参数detail存在则显示实力配置列表详细信息,若不传仅显示概要列表
*
* @example GET /openstack/blcokstorage/volume?{detail}
*
*/
async index() {
const { ctx } = this;
const RESULT = await this.volumeMODEL.getUsers(this.auth.id, this.QMFN({...ctx.query }));
ctx.body = RESULT;
};
/**
* @name 获取单一磁盘
*
* @description
*
* @example GET /openstack/blcokstorage/volume/{id}
*
*/
async show() {
const ctx = this.ctx;
// const datas = await this.volumeSERVICE.show(ctx.params.id, this.auth.id);
// ctx.body = datas;
const RESULT = await this.volumeMODEL.getOne(this.auth.id, ctx.params.id, ctx.isAdmin())
ctx.body = RESULT;
};
/**
* @name 创建磁盘
*
* @description
*
* @example POST /openstack/blcokstorage/volume
*
* @param {String} size 大小
* @param {String} availability_zone Optional 可用区域
* @param {String} snapshot_id Optional 快照ID FN从快照创建
* @param {String} backup_id Optional 备份ID FN从备份创建
* @param {String} imageRef Optional 镜像ID FN从镜像创建
* @param {String} volume_type Optional 磁盘类型 默认HDD
*
*/
async create() {
const { ctx } = this;
const { availability_zone, snapshot_id, backup_id, imageRef, size = 5, volume_type = "hdd" } = ctx.request.body;
const BODYS = this.DUFN({ availability_zone, snapshot_id, backup_id, imageRef, size, volume_type })
const datas = await this.volumeMODEL.createOne(this.auth.id, BODYS)
ctx.body = datas
};
/**
* @name 调整磁盘
*
* @description
*
* @example POST /openstack/blcokstorage/volume
*
* @param {String} size 大小
* @param {String} availability_zone Optional 可用区域
* @param {String} snapshot_id Optional 快照ID FN从快照创建
* @param {String} backup_id Optional 备份ID FN从备份创建
* @param {String} imageRef Optional 镜像ID FN从镜像创建
* @param {String} volume_type Optional 磁盘类型 默认HDD
*
*/
async update() {
const ctx = this.ctx;
const BODYS = ctx.request.body;
const { action, name } = BODYS;
delete BODYS.action;
const datas = !!action ? await this.volumeSERVICE.action(this.auth.id, this.auth.ProjectID, ctx.params.id, action, BODYS) : await this.serverSERVICE.update(ctx.params.id, this.auth.id, this.DUFn({ name }));
ctx.body = datas
};
/**
* @name 删除磁盘
*
* @description
*
*/
async destroy() {
const ctx = this.ctx;
const DATAS = await this.volumeSERVICE.destroy(this.auth.id, this.auth.ProjectID, ctx.params.id);
ctx.body = DATAS
}

}

module.exports = OPBlcokStorageVolumeController;
model
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const ctx = app.createAnonymousContext();
const {
helper,
service
} = app.createAnonymousContext();
const {
NumToStr
} = ctx.helper;
const OSblockStorageVolumeSchema = new Schema({
// 配置
config: {},
// 公司id
_comp: {
type: Schema.Types.ObjectId,
ref: 'Company',
required: true,
index: true
},
// 支付方式
payment: {
type: String,
default: 'prepay'
},
// 到期时间
endTime: {
type: Date
},
// 后台备注描述
_description: {

},
// 源信息
_metadata: {
type: String,
index: true
},
/*---------------------------------------------------*/
"status": {
type: 'String',
default: 'creating'
},
"migration_status": {},
"user_id": {},
"attachments": ,
"availability_zone": {},
"bootable": {},
"encrypted": {},
"created_at": {},
"description": {},
"updated_at": {},
"volume_type": {},
"name": {},
"replication_status": {},
"consistencygroup_id": {},
"source_volid": {},
"imageRef": {},
"backup_id": {},
"snapshot_id": {},
"multiattach": {},
"metadata": {},
"id": {
type: String,
index: true
},
"size": {},
"os-vol-host-attr:host": {},
"os-vol-tenant-attr:tenant_id": {}
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
OSblockStorageVolumeSchema.statics = {
getUsers: async function(_comp, queryMix) {
const serverSERVICE = ctx.service.openstack.server.servers;
const {
querys,
select,
sort,
limit,
marker
} = queryMix;
const withDetail = querys.detail === '' || querys.detail;
delete querys.detail;
let QUERTS = {};
for (const key in querys) {
QUERTS[key] = eval(`/${querys[key]}.*/i`)
}
const MODELS = (count = false) => this.find({
_comp,
...QUERTS,
...(marker && !count && {
'_id': {
"$lt": marker
}
}),
}, {
...select,
}, {
...!count && {
limit
},
'sort': {
_id: -1,
...sort
}
});
try {
const datas = await MODELS()
return {
data: {
result: datas,
limit,
marker: datas.length ? [...datas].pop()._id : '',
totalCount: await MODELS(true).count()
}
}
} catch (error) {
return error
}
},
/**
* @name 创建硬盘
* @param {*} _id
* @param {*} body
*/
createOne: async function(_id, bodys) {
try {
const {
endTime,
payment,
_metadata
} = bodys;
delete bodys._metadata;
delete bodys.endTime;
delete bodys.payment;
//
const DATAS = await service.openstack.blcokStorage.volume.create(_id, {
...bodys,
... {
"name": `volume-${Math.random().toString(36).slice(2, 8)}`
}
});
const {
id: _openstack_id
} = DATAS.data.volume;
!!DATAS.data.volume && await ctx.model.Schedule.Openstack.Volume.addOne({
_comp: _id,
_openstack_id,
type: 'volume',
active: 'creating',
_metadata
})
return !!DATAS.data.volume ? {
data: await this.create({
_comp: _id,
endTime,
payment,
_metadata,
...DATAS.data.volume
})
} : {
'code': 422,
'message': '创建失败'
};
} catch (error) {
return error
}
},
// 获取全部
getAll: async function(queryMix) {
const {
querys,
select,
pages,
sort,
dates
} = queryMix;
const MODELS = this.find({
'status': {
'$ne': 0
},
...querys,
// ...(!!pages && !!pages.marker && { '_id': { "$lt": pages.marker } }),
}, {
...select,
})
// .limit(!!pages ? pages.limit : 10)
.sort({
// '_id': -1,
'id': 1,
...sort
});
return {
data: {
result: await MODELS,
totalCount: await MODELS.count()
}
};
},
// 查看单个
getOne: async function(_comp, id, isAdmin) {
const MODELS = this.findOne({...isAdmin ? {
id
} : {
_comp,
id
}
});
try {
const RESULT = await MODELS;
return {
data: RESULT
}
} catch (error) {
return error
}
},
// 绑定到磁盘
/**
* @name 绑定到磁盘
* @param {*} _comp 用户UUID
* @param {*} id 磁盘ID
* @param {*} type 'bind'|'unbind' 绑定方式
*/
bindToECS: async function({
_comp,
id,
type = "bind"
} = {}, bodys) {
const MODEL = this.findOneAndUpdate({
_comp,
id
}, {
'attachments': type === 'bind' ? [bodys] : ,
'status': type === 'bind' ? 'in-use' : 'available'
});
try {
const datas = await MODEL;
} catch (error) {
return error
}
}
}
return mongoose.model('openstack_block_storage_volume', OSblockStorageVolumeSchema)
}
service
'use strict';
const ServerIndex = require('../index')
//
class VolumeService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.adminprojectID = this.config.openstack.projectID.default;
this.actions = ':8776/v3';
};
// 获取磁盘列表
async list(querys, _ucid, project_id, isAdmin) {
try {
const detail = Object.keys(querys).includes('detail');
delete querys.detail;
let SORT = {}
if (querys.sort_key) {
SORT = { 'sort': `${querys.sort_key}:${querys.sort_dir}` };
delete querys.sort_key;
delete querys.sort_dir;
}
const datas = await this.OSAJax(`${this.actions}/${!!isAdmin ? this.adminprojectID : project_id}/volumes${detail ? '/detail' : ''}`, {
...!isAdmin && { _ucid },
body: {
...querys,
...querys.sort_key && SORT,
...isAdmin && { 'all_tenants': true },
},
});
return {
data: {
result: datas.volumes.map(e => {
!!detail && e.attachments.length && (e.attachment = e.attachments[0].server_id)
delete e.links;
delete e.attachments;
return e
}).filter(e => e.status != 'deleting'),
totalCount: datas.volumes.length
}
};
} catch (error) {
return error
}
}
// 获取磁盘详情
async show(id, _ucid, _description) {
try {
const datas = await this.OSAJax(`${this.actions}/${await this.getProject(_ucid)}/volumes/${id}`, {
..._ucid !== 'admin' && { _ucid }
});
return _ucid === 'admin' ? { ...datas.volume, _description } : { data: datas.volume };
} catch (error) {
return error
}
}
// 创建磁盘
async create(_ucid, bodys, order = "false") {
const DATAS = this.OSAJax(`${this.actions}/${await this.getProject(_ucid)}/volumes`, {
body: {
'volume': {
... {
"availability_zone": null,
"source_volid": null,
"description": null,
"multiattach": false,
"snapshot_id": null,
"backup_id": null,
"imageRef": null,
"metadata": {},
"consistencygroup_id": null
},
...bodys
}
},
method: 'POST',
_ucid
});
try {
const datas = await DATAS
return !!order ? { data: datas } : datas
} catch (error) {
return error
}

}
// 删除磁盘
async destroy(_ucid, project_id, volume_id) {
try {
const datas = await this.OSAJax(`${this.actions}/${project_id}/volumes/${volume_id}`, {
_ucid,
method: 'DELETE',
full: true
});
return {
...datas.status === 404 && {
message: `${volume_id}不存在或已删除`
}
}
} catch (error) {

}
}
// 磁盘操作
// {'os-extend':'磁盘扩容','revert':'镜像回滚'}
async action(_ucid, project_id, volume_id, type, bodys) {
const isIn = (e) => ['os-extend', 'revert'].includes(e);
if (!isIn(type)) return { code: 422 };
const actionMAP = (e) => {
const OBJ = {};
OBJ[e] = !bodys ? null : bodys;
return OBJ;
}
try {
const DATAS = await this.OSAJax(`${this.actions}/${project_id}/volumes/${volume_id}/action`, {
body: actionMAP(type),
method: 'POST',
_ucid, full: true
});
return {
code: DATAS.status
}
} catch (error) {
return error
}
}
async update(_ucid, project_id, volume_id, bodys) {
try {
const DATAS = await this.OSAJax(`${this.actions}/${project_id}/volumes/${volume_id}`, {
_ucid,
bodys: {
...bodys
},
method: 'PUT'
});
return {
data: DATAS
}
} catch (error) {
return error
}
}
}
module.exports = VolumeService;

schedule
const Subscription = require('egg').Subscription;

class getVolumeStatus extends Subscription {
constructor(ctx) {
super(ctx);
this.SERVICE = ctx.service.openstack.blcokStorage.volume;
this.MODELS = ctx.model.Schedule.Openstack.Volume;
this.volumeMODEL = ctx.model.Openstack.BlcokStorage.Volumes;
}
static get schedule() {
return {
interval: '10s',
type: 'worker',
};
}
async subscribe() {
const ctx = this.ctx;
const lists = await this.MODELS.getALL();
if (lists.length) {
const datas = await this.checkItem(lists);
}
}
async checkItem(result) {
// 调取记录
const checkFN = result.map(e => {
// 记录尝试次数
this.MODELS.retry(e._openstack_id)
return this.SERVICE.show(e._openstack_id, 'admin', e);
})
let DATAS = await Promise.all(checkFN);
// 检查ACTION
if (!!DATAS.length) {
const endOrder = DATAS.map(e => {
const { _comp, _openstack_id: id } = e._description;
delete e._description
delete e.links
if (e.status === 'available' || e.status === 'in-use') {
return this.volumeMODEL.findOneAndUpdate({ id }, { ...e });
}
})
DATAS = await Promise.all(endOrder);
};
// 清除已完成任务
if (DATAS.length) {
const clearSchedule = DATAS.map(e => {
if (!!e) {
const { id: _openstack_id } = e;
return this.MODELS.deleteOne({ '_openstack_id': _openstack_id })
}
})
DATAS = await Promise.all(clearSchedule);
}
}
}

module.exports = getVolumeStatus;

schedule model
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const ctx = app.createAnonymousContext();
const scheduleOSVolumeSchema = new Schema({
// 类型
type: {
type: String,
},
// 尝试次数
retry: {
type: Number,
dafault: 0
},
// 是否创建成功
status: {
type: Boolean,
default: false
},
// 操作方式 [BUILD:创建,DELETE:删除]
active: {
type: String,
},
// 数据ID
_openstack_id: {
type: String,
index: true
},
// 公司ID
_comp: {
type: String
},
// 其他配置参数
/**
* ecs:Number
*/
_description: {

},
// 其他配置元信息
_metadata: {

}
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
scheduleOSVolumeSchema.statics = {
getALL: async function (limit = 50) {
const MODEL = this.find().limit(limit);
try {
return await MODEL;
} catch (error) {
return error
}
},
addOne: async function (bodys) {
const MODEL = this.create({ ...bodys });
try {
const result = await MODEL;
return { code: !!result ? 201 : 404 }
} catch (error) {
return error
}
},
// destroyOne: async function (id) {
// const MODEL = this.deleteOne(id);
// try {
// const result = await MODEL
// return result
// } catch (error) {
// return error
// }
// },
retry: async function (_openstack_id) {
const MODEL = this.findOneAndUpdate({ _openstack_id }, {
'$inc': { retry: 1 }
});
try {
await MODEL
} catch (error) {
return error
}
}
}
return mongoose.model('schedule_openstack_volume', scheduleOSVolumeSchema);
}

egg.js+mongodb+openstack 公有云计费系统(三)用户系统的搭建 (2)

Nodejslopo1983 发表了文章 • 0 个评论 • 1508 次浏览 • 2019-03-15 11:08 • 来自相关话题

A/SK 机制
?
控制器
?
/*************************************************************
*
*- Copyright (c) qiduo, 2018
*- FileName: AccesskeyController.js 安全认证 Accesskey接口
*- Author: 罗波 lopo1983@vip.qq.com
*- Version: 1.0
*- Date:2018-10-15
*- Descripttion:openstack geecp
*- Modules:
*
*- Extends
* - 1.egg_Controller
*----------------------------------------------------------
*
*- Function List :
*
*- StaticsList :
* - show 获取所有ak/sk
* - update 创建ak/sk 修改备注
* - destroy 删除ak/sk
*
*- History :
* <Author> <Date> <Desc>
*
**************************************************************/
'use strict';

const Controller = require('egg').Controller;

class AccesskeyController extends Controller {
/**
* @name constructor
*
* @param {*} ctx
* @param {Function} DuFn 清理空字段函数
* @param {Model} MODEL 公司表
* @param {Object} auth 用户JWT
*
*/
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.DUFn = ctx.helper.deleteUndefined;
this.MODEL = ctx.model.Account.Company;
}
/**
*
* @name 获取Accesskey列表
*
* @example
* - GET /user/accesskey/_id{ObjectId} HTTP/1.1
*
*/
async show() {
const ctx = this.ctx;
const result = await this.MODEL.getASK({ '_id': ctx.params.id });
ctx.body = result
}
/**
*
* @name 创建AK/SK|修改Info
*
* @param {String} info 必填 备注内容
* @param {ObjectId} id 必填 A/SK{ObjectId}
*
* @example
* - PUT /user/accesskey/user{ObjectId} HTTP/1.1 创建
* - PUT /user/accesskey/user{ObjectId} HTTP/1.1 修改
* body:{
* id:ak/sk{ObjectId}
* info:备注名称
* }
*
* @description info id 为必填参数
*
*/
async update() {
const ctx = this.ctx;
if (!!ctx.request.body.info) {
ctx.body = await this.MODEL.setInfo(ctx);
}
else {
ctx.body = await this.MODEL.createASK(ctx);
}
}
/**
*
* @name 删除AK/SK
*
* @param {ObjectId} id 必填 A/SK id
*
* @example
* PUT /user/accesskey/id{ObjectId} HTTP/1.1
* @description id 为必填参数
*
*/
async destroy() {
const ctx = this.ctx;
ctx.body = await this.MODEL.delASK(ctx);
}
/**
*
* @name ak/sk鉴权接口
*
* @param {ObjectId} uid 必填 用户uid
* @param {String} api_key 必填 accountKey
* @param {String} secret_key 必填 secretKey
*
* @example
* POST /user/authorization HTTP/1.1
*
* @description uid|api_key|secret_key 必填
*
*/
async authorization() {
const ctx = this.ctx;
const { uid, api_key, secret_key } = ctx.request.body;
ctx.body = await this.MODEL.apiToken({ uid, api_key, secret_key });
}
}

module.exports = AccesskeyController;

用户鉴权
控制器
'use strict';
const Controller = require('egg').Controller;
class ServiceJwtController extends Controller {
constructor(ctx) {
super(ctx);
this.MODELComp = ctx.model.Account.Company;
this.MODELAdmin = ctx.model.Account.Admin;
};
/**
* @name 用户token生成
*
* @param {action:string} 'api' 用于鉴权ak/sk生成token
* @param {String} compEmail 用户邮箱
* @param {String} compPhone 用户手机
* @param {String} compPassword 用户密码
*
* @description
*
* @example POST /user/sign/
*
* @return Object
*{
* "token": "",
* "info": {
* "_id": "5bcdd5e7f12ee030f44b6228",
* "compPhone": "13658157663",
* "compEmail": "64832897@qq.com",
* "compAuth": {
* "email": false,
* "phone": true
* },
* "compService": [],
* "updated": "2018-10-22T13:51:35.314Z",
* "compApi": []
* }
* }
*
**/
async sign() {
const ctx = this.ctx;
const {
body,
header
} = ctx.request;
try {
if (ctx.request.url.includes('admin')) {
ctx.body = await this.MODELAdmin.isLogin(ctx);
} else {
if (!['compEmail', 'compPhone'].map(e => Object.keys(body).includes(e)).reduce((a, b) => a + b)) {
ctx.body = {
code: 422,
message: '用户参数错误'
};
return
};
ctx.body = await this.MODELComp.isLogin(ctx);
}
} catch (error) {
console.log(error)
if (error.message === 'jwt expired') {
ctx.body = {
message: '用户鉴权失败,请重新登陆',
code: 401
}
} else {
ctx.body = {
message: !!error.errors ? `${error.errors.reduce((a, error) => { a.push(`${error.field} ${error.message}`); return a }, [])}` : error.message,
code: 422
}
}
}
}
}
module.exports = ServiceJwtControllerextend 扩展
简单用户划分
module.exports = {
isAdmin() {
console.log(this.url)
return this.state.user.group === 1 ? false : true;
},
};OpenStack 相关控制器
project
'use strict';

const Controller = require('egg').Controller;

class OPIdentityProjectsController extends Controller {
/**
* @name constructor
*
* @param {*} ctx
* @param {Function} DuFn 清理空字段函数
* @param {Object} auth 用户JWT
*
*/
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.DUFn = ctx.helper.deleteUndefined;
}
/**
* @name project列表
*
* @param {String} ctx.query 不需要参数
*
* @description query参数detail存在则显示实力配置列表详细信息,若不传仅显示概要列表
*
* @example GET /openstack/projects
*
*/
async index() {
const { ctx, service } = this;
const datas = await service.openstack.identity.projects.list(ctx.query);
ctx.body = datas;
}
/**
* @name 获取单一project信息
*
* @param {String} ctx.params.id 实例id
*
* @example GET /openstack/projects/{ctx.params.id}
*
*/
async show() {
const { ctx, service } = this;
const datas = await service.openstack.identity.projects.show(ctx.params.id);
ctx.body = datas;
}
/**
* @name 创建project
*
* @param {String} ctx.params.id 实例id
*
* @example
* - POST /openstack/projects/
*/
async create() {
const { ctx, service } = this;
const { name, is_domain, description, domain_id, enabled, parent_id, tags } = ctx.request.body;
const bodys = { name, is_domain, description, domain_id, enabled, parent_id, tags };
const datas = await service.openstack.identity.projects.create(this.DUFn(bodys))
ctx.body = datas;
}
/**
* @name 更新project
*
* @param {String} ctx.params.id 实例id
*
* @example
* - DELETE /openstack/projects/{ctx.params.id}
*/
async destroy() {
const { ctx, service } = this;
const datas = await service.openstack.identity.projects.destroy(ctx.params.id);
ctx.body = datas;
}
/**
* @name 删除project
*
* @param {String} ctx.params.id 实例id
*
* @example
* - PATCH /openstack/projects/{ctx.params.id}
*/
async update() {
const { ctx, service } = this;
const { name, is_domain, description, domain_id, enabled, parent_id, tags } = ctx.request.body;
const bodys = { name, is_domain, description, domain_id, enabled, parent_id, tags };
const datas = await service.openstack.identity.projects.update(ctx.params.id, this.DUFn(bodys))
ctx.body = datas;
}
/**
* @name 锁定不可操作项目
*
*/
checkID(idx) {
const ARR = ['c3513f27bbf24362b74d13e6afae2c37', '5d3b50c18fd44db4bc6abfdbbfcf6a3a', '5c7e341df8ff493c8ae7baf57c0129dd'];
return ARR.includes(idx);
}
}

module.exports = OPIdentityProjectsController;
region
'use strict';

const Controller = require('egg').Controller;

class identityUserRegionController extends Controller {
/**
* @name constructor
*
* @param {*} ctx
* @param {Function} DuFn 清理空字段函数
* @param {Object} auth 用户JWT
*
*/
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.DUFn = ctx.helper.deleteUndefined;
this.MODEL = ctx.model.Openstack.Identity.Region
this.TKMODEL = ctx.model.Openstack.Token
};
/**
*
* @name 用户列表
*
* @description
*
* @example GET /openstack/identity/region
*
*/
async index() {
const { ctx, service } = this;
const datas = await this.MODEL.getAll(this.auth.id, ctx.isAdmin());
ctx.body = datas;
}
}

module.exports = identityUserRegionController;user
'use strict';

const Controller = require('egg').Controller;

class identityUserController extends Controller {
/**
* @name constructor
*
* @param {*} ctx
* @param {Function} DuFn 清理空字段函数
* @param {Object} auth 用户JWT
*
*/
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.DUFn = ctx.helper.deleteUndefined;
this.MODEL = ctx.model.Openstack.Identity.User
this.TKMODEL = ctx.model.Openstack.Token
};
/**
*
* @name 用户列表
*
* @param {String} ctx.query.detail 不需要参数
*
* @description query参数detail存在则显示实力配置列表详细信息,若不传仅显示概要列表
*
* @example GET /openstack/user
*
*/
async index() {
const { ctx, service } = this;
ctx.body = await service.openstack.identity.users.list(ctx.query);
};
/**
* @name 获取单一用户信息
*
* @param {String} ctx.params.id 实例id
*
* @example GET /openstack/user/{ctx.params.id}
*
*/
async show() {
const { ctx, service } = this;
ctx.body = await service.openstack.identity.users.show(ctx.params.id);
// const result = await this.MODEL.getOne(this.auth.id);
// ctx.body = result;
};
/**
* @name 创建用户
*
* @param {String} ctx.params.id 实例id
*
* @example
* - POST /openstack/user/
*/
async create() {
const ctx = this.ctx;
const result = await this.MODEL.createUser(this.auth.id)
ctx.body = result;
// const { ctx, service } = this;
// const { default_project_id, domain_id, description, enabled = true, email, name, password } = ctx.request.body;
// const bodys = { default_project_id, domain_id, description, enabled, email, name, password };
// ctx.body = await service.openstack.identity.users.create(this.DUFn(bodys));
};
/**
*
* @name 测试内部用token
*
*/
async testToken() {
const { ctx, service } = this;
ctx.body = await this.TKMODEL.getToken(this.auth.id)
};
}

module.exports = identityUserController; 查看全部
A/SK 机制
?
控制器
?
/*************************************************************
*
*- Copyright (c) qiduo, 2018
*- FileName: AccesskeyController.js 安全认证 Accesskey接口
*- Author: 罗波 lopo1983@vip.qq.com
*- Version: 1.0
*- Date:2018-10-15
*- Descripttion:openstack geecp
*- Modules:
*
*- Extends
* - 1.egg_Controller
*----------------------------------------------------------
*
*- Function List :
*
*- StaticsList :
* - show 获取所有ak/sk
* - update 创建ak/sk 修改备注
* - destroy 删除ak/sk
*
*- History :
* <Author> <Date> <Desc>
*
**************************************************************/
'use strict';

const Controller = require('egg').Controller;

class AccesskeyController extends Controller {
/**
* @name constructor
*
* @param {*} ctx
* @param {Function} DuFn 清理空字段函数
* @param {Model} MODEL 公司表
* @param {Object} auth 用户JWT
*
*/
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.DUFn = ctx.helper.deleteUndefined;
this.MODEL = ctx.model.Account.Company;
}
/**
*
* @name 获取Accesskey列表
*
* @example
* - GET /user/accesskey/_id{ObjectId} HTTP/1.1
*
*/
async show() {
const ctx = this.ctx;
const result = await this.MODEL.getASK({ '_id': ctx.params.id });
ctx.body = result
}
/**
*
* @name 创建AK/SK|修改Info
*
* @param {String} info 必填 备注内容
* @param {ObjectId} id 必填 A/SK{ObjectId}
*
* @example
* - PUT /user/accesskey/user{ObjectId} HTTP/1.1 创建
* - PUT /user/accesskey/user{ObjectId} HTTP/1.1 修改
* body:{
* id:ak/sk{ObjectId}
* info:备注名称
* }
*
* @description info id 为必填参数
*
*/
async update() {
const ctx = this.ctx;
if (!!ctx.request.body.info) {
ctx.body = await this.MODEL.setInfo(ctx);
}
else {
ctx.body = await this.MODEL.createASK(ctx);
}
}
/**
*
* @name 删除AK/SK
*
* @param {ObjectId} id 必填 A/SK id
*
* @example
* PUT /user/accesskey/id{ObjectId} HTTP/1.1
* @description id 为必填参数
*
*/
async destroy() {
const ctx = this.ctx;
ctx.body = await this.MODEL.delASK(ctx);
}
/**
*
* @name ak/sk鉴权接口
*
* @param {ObjectId} uid 必填 用户uid
* @param {String} api_key 必填 accountKey
* @param {String} secret_key 必填 secretKey
*
* @example
* POST /user/authorization HTTP/1.1
*
* @description uid|api_key|secret_key 必填
*
*/
async authorization() {
const ctx = this.ctx;
const { uid, api_key, secret_key } = ctx.request.body;
ctx.body = await this.MODEL.apiToken({ uid, api_key, secret_key });
}
}

module.exports = AccesskeyController;

用户鉴权
控制器
'use strict';
const Controller = require('egg').Controller;
class ServiceJwtController extends Controller {
constructor(ctx) {
super(ctx);
this.MODELComp = ctx.model.Account.Company;
this.MODELAdmin = ctx.model.Account.Admin;
};
/**
* @name 用户token生成
*
* @param {action:string} 'api' 用于鉴权ak/sk生成token
* @param {String} compEmail 用户邮箱
* @param {String} compPhone 用户手机
* @param {String} compPassword 用户密码
*
* @description
*
* @example POST /user/sign/
*
* @return Object
*{
* "token": "",
* "info": {
* "_id": "5bcdd5e7f12ee030f44b6228",
* "compPhone": "13658157663",
* "compEmail": "64832897@qq.com",
* "compAuth": {
* "email": false,
* "phone": true
* },
* "compService": [],
* "updated": "2018-10-22T13:51:35.314Z",
* "compApi": []
* }
* }
*
**/
async sign() {
const ctx = this.ctx;
const {
body,
header
} = ctx.request;
try {
if (ctx.request.url.includes('admin')) {
ctx.body = await this.MODELAdmin.isLogin(ctx);
} else {
if (!['compEmail', 'compPhone'].map(e => Object.keys(body).includes(e)).reduce((a, b) => a + b)) {
ctx.body = {
code: 422,
message: '用户参数错误'
};
return
};
ctx.body = await this.MODELComp.isLogin(ctx);
}
} catch (error) {
console.log(error)
if (error.message === 'jwt expired') {
ctx.body = {
message: '用户鉴权失败,请重新登陆',
code: 401
}
} else {
ctx.body = {
message: !!error.errors ? `${error.errors.reduce((a, error) => { a.push(`${error.field} ${error.message}`); return a }, [])}` : error.message,
code: 422
}
}
}
}
}
module.exports = ServiceJwtController
extend 扩展
简单用户划分
module.exports = {
isAdmin() {
console.log(this.url)
return this.state.user.group === 1 ? false : true;
},
};
OpenStack 相关控制器
project
'use strict';

const Controller = require('egg').Controller;

class OPIdentityProjectsController extends Controller {
/**
* @name constructor
*
* @param {*} ctx
* @param {Function} DuFn 清理空字段函数
* @param {Object} auth 用户JWT
*
*/
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.DUFn = ctx.helper.deleteUndefined;
}
/**
* @name project列表
*
* @param {String} ctx.query 不需要参数
*
* @description query参数detail存在则显示实力配置列表详细信息,若不传仅显示概要列表
*
* @example GET /openstack/projects
*
*/
async index() {
const { ctx, service } = this;
const datas = await service.openstack.identity.projects.list(ctx.query);
ctx.body = datas;
}
/**
* @name 获取单一project信息
*
* @param {String} ctx.params.id 实例id
*
* @example GET /openstack/projects/{ctx.params.id}
*
*/
async show() {
const { ctx, service } = this;
const datas = await service.openstack.identity.projects.show(ctx.params.id);
ctx.body = datas;
}
/**
* @name 创建project
*
* @param {String} ctx.params.id 实例id
*
* @example
* - POST /openstack/projects/
*/
async create() {
const { ctx, service } = this;
const { name, is_domain, description, domain_id, enabled, parent_id, tags } = ctx.request.body;
const bodys = { name, is_domain, description, domain_id, enabled, parent_id, tags };
const datas = await service.openstack.identity.projects.create(this.DUFn(bodys))
ctx.body = datas;
}
/**
* @name 更新project
*
* @param {String} ctx.params.id 实例id
*
* @example
* - DELETE /openstack/projects/{ctx.params.id}
*/
async destroy() {
const { ctx, service } = this;
const datas = await service.openstack.identity.projects.destroy(ctx.params.id);
ctx.body = datas;
}
/**
* @name 删除project
*
* @param {String} ctx.params.id 实例id
*
* @example
* - PATCH /openstack/projects/{ctx.params.id}
*/
async update() {
const { ctx, service } = this;
const { name, is_domain, description, domain_id, enabled, parent_id, tags } = ctx.request.body;
const bodys = { name, is_domain, description, domain_id, enabled, parent_id, tags };
const datas = await service.openstack.identity.projects.update(ctx.params.id, this.DUFn(bodys))
ctx.body = datas;
}
/**
* @name 锁定不可操作项目
*
*/
checkID(idx) {
const ARR = ['c3513f27bbf24362b74d13e6afae2c37', '5d3b50c18fd44db4bc6abfdbbfcf6a3a', '5c7e341df8ff493c8ae7baf57c0129dd'];
return ARR.includes(idx);
}
}

module.exports = OPIdentityProjectsController;
region
'use strict';

const Controller = require('egg').Controller;

class identityUserRegionController extends Controller {
/**
* @name constructor
*
* @param {*} ctx
* @param {Function} DuFn 清理空字段函数
* @param {Object} auth 用户JWT
*
*/
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.DUFn = ctx.helper.deleteUndefined;
this.MODEL = ctx.model.Openstack.Identity.Region
this.TKMODEL = ctx.model.Openstack.Token
};
/**
*
* @name 用户列表
*
* @description
*
* @example GET /openstack/identity/region
*
*/
async index() {
const { ctx, service } = this;
const datas = await this.MODEL.getAll(this.auth.id, ctx.isAdmin());
ctx.body = datas;
}
}

module.exports = identityUserRegionController;
user
'use strict';

const Controller = require('egg').Controller;

class identityUserController extends Controller {
/**
* @name constructor
*
* @param {*} ctx
* @param {Function} DuFn 清理空字段函数
* @param {Object} auth 用户JWT
*
*/
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.DUFn = ctx.helper.deleteUndefined;
this.MODEL = ctx.model.Openstack.Identity.User
this.TKMODEL = ctx.model.Openstack.Token
};
/**
*
* @name 用户列表
*
* @param {String} ctx.query.detail 不需要参数
*
* @description query参数detail存在则显示实力配置列表详细信息,若不传仅显示概要列表
*
* @example GET /openstack/user
*
*/
async index() {
const { ctx, service } = this;
ctx.body = await service.openstack.identity.users.list(ctx.query);
};
/**
* @name 获取单一用户信息
*
* @param {String} ctx.params.id 实例id
*
* @example GET /openstack/user/{ctx.params.id}
*
*/
async show() {
const { ctx, service } = this;
ctx.body = await service.openstack.identity.users.show(ctx.params.id);
// const result = await this.MODEL.getOne(this.auth.id);
// ctx.body = result;
};
/**
* @name 创建用户
*
* @param {String} ctx.params.id 实例id
*
* @example
* - POST /openstack/user/
*/
async create() {
const ctx = this.ctx;
const result = await this.MODEL.createUser(this.auth.id)
ctx.body = result;
// const { ctx, service } = this;
// const { default_project_id, domain_id, description, enabled = true, email, name, password } = ctx.request.body;
// const bodys = { default_project_id, domain_id, description, enabled, email, name, password };
// ctx.body = await service.openstack.identity.users.create(this.DUFn(bodys));
};
/**
*
* @name 测试内部用token
*
*/
async testToken() {
const { ctx, service } = this;
ctx.body = await this.TKMODEL.getToken(this.auth.id)
};
}

module.exports = identityUserController;

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

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

?























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

?
设置.png


资金.png


客户.png


辅助资料.png


部门与员工.png

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

baidu sdk 鉴权处理(nodejs)

Nodejslopo1983 发表了文章 • 0 个评论 • 1394 次浏览 • 2019-01-18 17:25 • 来自相关话题

比较坑 但是还是爬出来了 参考代码
const { Auth } = require('bce-sdk-js');

const accessKey = 'aaaaaaaaaaaaa';
const secretKey = 'sssssssssssss';
const bdauth = new Auth(accessKey, secretKey);
const HEADER = {
'Host': 'bcc.bj.baidubce.com',
'Content-Type': 'application/json; charset=UTF-8',
'x-bce-date': new Date().toISOString().replace(/\.\d+Z$/, 'Z')
};
const Authorization = bdauth.generateAuthorization('GET', '/v2/image', { maxKeys: 1000, imageType: 'All' }, HEADER);当然 感觉baidu 已经放弃node了
可以自己尝试撸下
class authorization {
constructor() {
this.accessKey = ask.ak;
this.secretKey = ask.sk;
this.host = 'bcc.bj.baidubce.com';
this.utcTimestamp = new Date().toISOString().replace(/\.\d+Z$/, 'Z');
this.expireTime = 1800;
}
getAuthorization(method, uri, params, header, body) {
let authStringPrefix = `bce-auth-v1/${this.accessKey}/${this.utcTimestamp}/${this.expireTime}`;
let SigningKey = crypto.createHmac('sha256', this.secretKey.toString('ascii')).update(authStringPrefix).digest('hex');
let HEADER = {
'host': this.host,
'content-type': 'application%2Fjson%3B+charset%3Dutf-8',
'x-bce-date': this.utcTimestamp,
...header
};
const 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;
};
HEADER = OBJKeySort(HEADER)
let headerArr = [];
for (let name in HEADER) {
let val = HEADER[name];
headerArr.push(`${name.toLowerCase()}:${val}`);
}
let signedHeaders = Object.keys(HEADER).join(';');
let requestStr = `${method}\n${uri}\n${params?qs.stringify(params)+'\n':''}${headerArr.join('\n')}`;
let Signature = crypto.createHmac('sha256', SigningKey.toString('ascii')).update(requestStr).digest('hex');
return `${authStringPrefix}/${signedHeaders}/${Signature}`;
}
}
module.exports = authorization; 查看全部
比较坑 但是还是爬出来了 参考代码
const { Auth } = require('bce-sdk-js');

const accessKey = 'aaaaaaaaaaaaa';
const secretKey = 'sssssssssssss';
const bdauth = new Auth(accessKey, secretKey);
const HEADER = {
'Host': 'bcc.bj.baidubce.com',
'Content-Type': 'application/json; charset=UTF-8',
'x-bce-date': new Date().toISOString().replace(/\.\d+Z$/, 'Z')
};
const Authorization = bdauth.generateAuthorization('GET', '/v2/image', { maxKeys: 1000, imageType: 'All' }, HEADER);
当然 感觉baidu 已经放弃node了
可以自己尝试撸下
class authorization {
constructor() {
this.accessKey = ask.ak;
this.secretKey = ask.sk;
this.host = 'bcc.bj.baidubce.com';
this.utcTimestamp = new Date().toISOString().replace(/\.\d+Z$/, 'Z');
this.expireTime = 1800;
}
getAuthorization(method, uri, params, header, body) {
let authStringPrefix = `bce-auth-v1/${this.accessKey}/${this.utcTimestamp}/${this.expireTime}`;
let SigningKey = crypto.createHmac('sha256', this.secretKey.toString('ascii')).update(authStringPrefix).digest('hex');
let HEADER = {
'host': this.host,
'content-type': 'application%2Fjson%3B+charset%3Dutf-8',
'x-bce-date': this.utcTimestamp,
...header
};
const 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;
};
HEADER = OBJKeySort(HEADER)
let headerArr = [];
for (let name in HEADER) {
let val = HEADER[name];
headerArr.push(`${name.toLowerCase()}:${val}`);
}
let signedHeaders = Object.keys(HEADER).join(';');
let requestStr = `${method}\n${uri}\n${params?qs.stringify(params)+'\n':''}${headerArr.join('\n')}`;
let Signature = crypto.createHmac('sha256', SigningKey.toString('ascii')).update(requestStr).digest('hex');
return `${authStringPrefix}/${signedHeaders}/${Signature}`;
}
}
module.exports = authorization;

node爬取ui中国图片

Nodejslopo1983 发表了文章 • 0 个评论 • 1424 次浏览 • 2019-01-17 10:46 • 来自相关话题

以下代码仅供学习交流用,切勿做其他用途
/*************************************************************
*
*- Copyright (c), 2018, lopo qq:64832897
*- FileName : default.js
*- Author : 罗波 Version : 1.0 Date:2018-10-14
*- Descripttion : 图片image
*- Other : www.ui.cn
*- JSVersion : ES6
*- Modules :
* 1.request
* 2.cheerio
* 3.fs
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
"use strict"
const request = require('request-promise-native');
const cheerio = require('cheerio');
const fs = require('fs');
const timeout = async(ms) => {
await new Promise((resolve) => {
setTimeout(resolve, ms);
});
};
module.exports = {
async getImg() {
for (let j = 0; j < 2; j++) {
let DATA = Object.assign({}, BACKJSON);
let typeArr = ['all', 'main', 'edit', 'gener'];
const options = {
uri: 'http://www.ui.cn/list.html',
transform: (body) => cheerio.load(body),
qs: {
'r': 'main',
'p': j
},
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36'
}
};
let setDATA = async($) => {
let arrs = [];
await $('.imgloadinglater').each((i, e) => {
arrs.push({
url: $(e).parent().attr(),
src: $(e).data('original'),
name: $(e).data('original').split('/').pop(),
title: $(e).parents('.cover').next().find('.title').text().trim(),
})
});
for (const e of arrs) {
await request(e.src).pipe(fs.createWriteStream(`d:/ui/uichina/${e.name}`));
await timeout(1500);
}
console.log(arrs)
};
try {
const data = await request(options);
const json = setDATA(data);
} catch (error) {
console.log(error)
}
await timeout(15000);
}
}
} 查看全部

以下代码仅供学习交流用,切勿做其他用途


/*************************************************************
*
*- Copyright (c), 2018, lopo qq:64832897
*- FileName : default.js
*- Author : 罗波 Version : 1.0 Date:2018-10-14
*- Descripttion : 图片image
*- Other : www.ui.cn
*- JSVersion : ES6
*- Modules :
* 1.request
* 2.cheerio
* 3.fs
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
"use strict"
const request = require('request-promise-native');
const cheerio = require('cheerio');
const fs = require('fs');
const timeout = async(ms) => {
await new Promise((resolve) => {
setTimeout(resolve, ms);
});
};
module.exports = {
async getImg() {
for (let j = 0; j < 2; j++) {
let DATA = Object.assign({}, BACKJSON);
let typeArr = ['all', 'main', 'edit', 'gener'];
const options = {
uri: 'http://www.ui.cn/list.html',
transform: (body) => cheerio.load(body),
qs: {
'r': 'main',
'p': j
},
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36'
}
};
let setDATA = async($) => {
let arrs = [];
await $('.imgloadinglater').each((i, e) => {
arrs.push({
url: $(e).parent().attr(),
src: $(e).data('original'),
name: $(e).data('original').split('/').pop(),
title: $(e).parents('.cover').next().find('.title').text().trim(),
})
});
for (const e of arrs) {
await request(e.src).pipe(fs.createWriteStream(`d:/ui/uichina/${e.name}`));
await timeout(1500);
}
console.log(arrs)
};
try {
const data = await request(options);
const json = setDATA(data);
} catch (error) {
console.log(error)
}
await timeout(15000);
}
}
}

用nodejs编写股票数据爬虫

Nodejslopo1983 发表了文章 • 0 个评论 • 1770 次浏览 • 2019-01-16 15:52 • 来自相关话题

以下代码仅仅用作学习交流,请勿用作其他用途
?基础类 getBoard
/*************************************************************
*
*- Copyright (c), 股票数据API接口基础Class, 2018, lopo
*- FileName : default.js
*- Author : 罗波 Version : 1.0 Date:2018-8-4
*- Descripttion : 股票 Node Express
*- Other : 数据来源-东方财富
*- JSVersion : ES6
*- Modules :
* 1.request
* 2.cheerio
*
*----------------------------------------------------------
*
*- Function List :
* - tools
* 1. getCodeExchange
*
*- Class List :
* 1. boards
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
'use strict'
const request = require('request-promise-native');
const { getCodeExchange } = require('../utils/tools');

/**
* 基础配置
*
* @class getBoards
*/
class getBoards {
// 基础参数
constructor() {
// - stockSty : ['列表概览','个股详细']
this.reqOpt = {
uri: 'http://nufm.dfcfw.com/EM_Finance2014NumericApplication/JS.aspx',
qs: {
type: 'CT',
token: '64a483cbad8b666efa51677820e6b21c',
js: '({data:[(x)],totle:(tot)})',
'_': (new Date()).valueOf(),
}
};
// _rp_options.qs
this.request = request.defaults(this.reqOpt);
// - pageFn : p/当前页 ps/页数据量 st/排序字段 sr/排序方式
//* @param {Number} p 当前页
//* @param {Number} ps 每页数量 20
//* @param {String} st 排序字段
//* @param {Number} sr 排序方式 -1,1
this.pageOpt = (params) => {
return {
qs: {
p: params[0] || '1',
ps: params[1] || '20',
st: params[2] || '(ChangePercent)',
sr: params[3] || '-1'
}
}
};
/**
*
* @param {Objec} cmdMap 参数比对数组
* @param {Object} params page参数
* @param {String} type 参数
* @param {String} sty 展示方式
*
* @returns promise
*
* @memberof boards
*/
this.boradFn = (cmdMap, ...params) => (type, sty = this.stockSty[0]) => {
const boardMap = cmdMap;
if (!boardMap[type]) return { 'err': 'TypeError' };
const opt = this.pageOpt([...params]);
Object.assign(opt.qs, {
'cmd': boardMap[type],
'sty': sty
});
return this.getDatas({...opt })
};
};
/**
*格式化cmd配置亲求
*
* @param {String,Arrary} codes 股票代码格式化
* @returns String
* @memberof boards
*/
setCodeCmd(codes) {
if (codes.includes(',')) codes = codes.split(',');
return Array.isArray(codes) ? codes.reduce((a, b, i) => a += `${getCodeExchange(b)}${i<codes.length-1?',':''}`, '') : `${getCodeExchange(codes)}`
};
/**
*request 请求函数
*
* @param {Object} opt 请求配置
* @returns promise
* @memberof boards
*/
async getDatas(opt) {
try {
const datas = await this.request({...opt });
return await eval(datas);
} catch (error) {
return { 'err': 'NETError' }
}
};
}
module.exports = getBoards辅助类 tool.js
const fixMoney = (money, fix = 2) => (money / (money.length < 8 ? 10000 : 100000000)).toFixed(fix) + (money.length < 8 ? '万' : '亿');
//
const timeout = async(ms) => {
await new Promise((resolve) => {
setTimeout(resolve, ms);
});
};
const getCodeExchange = (code, type = "stock") => {
let codes = code.substr(0, 3);
if (type === 'stock') return (codes === '000' || codes === '002') && `${code}2` || (codes === '600' || codes === '601' || codes === '603') && `${code}1` || `${code}2`
else return codes === '399' ? `${code}2` : `${code}1`
};
module.exports = {
fixMoney,
timeout,
getCodeExchange
}基金 fund.js
/*************************************************************
*
*- Copyright (c), 股票数据API接口 基金, 2018, lopo
*- FileName : default.js
*- Author : 罗波 Version : 1.0 Date:2018-8-4
*- Descripttion : 股票 Node Express
*- Other : 数据来源-东方财富
*- JSVersion : ES6
*
*----------------------------------------------------------
*- Class List :
* 1.getBoard
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
'use strict'
const getBoard = require('../../utils/getBoard');
//
class funds extends getBoard {
/**
* 封闭基金
*
* @param {string} [type='ALL']
* @param {*} params
* @returns
* @memberof futures
*/
// 封闭基金 ETF基金 LOF基金
async FUND_Board(type = 'ALL', ...params) {
const ENERGYMap = {
'CLOSE_END': 'C.__285002',
'ETF': 'C.__2850013',
'LOF': 'C.__2850014'
};
const datas = this.boradFn(ENERGYMap, ...params);
return datas(type, 'FCOIATC')
};

}
const client = new funds();?
// client.FE_DCE_Board('ALL', 1, 50).then(res => console.log(res));
client.FUND_Board('CLOSE_END', 1, 50).then(res => console.log(res))期货类 futures.js
/*************************************************************
*
*- Copyright (c), 股票数据API接口 期货, 2018, lopo
*- FileName : default.js
*- Author : 罗波 Version : 1.0 Date:2018-8-4
*- Descripttion : 股票 Node Express
*- Other : 数据来源-东方财富
*- JSVersion : ES6
*
*----------------------------------------------------------
*- Class List :
* 1.getBoard
*
*- Class List :
* 1. futures
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
'use strict'
const getBoard = require('../../utils/getBoard');
//
class futures extends getBoard {
constructor() {
super();
this.fecSty = ['FCHKEGL', 'FCFL4O']
};
/**
* 国内期货数据
*
* @param {*} type
* @param {*} params
* @returns
* @memberof boards
*/
//[上期所所有,["沪锌", "沪铅", "石油沥青", "沪金", "螺纹钢", "沪银", "热轧卷板", "沪铝", "沪锡", "橡胶", "沪铜", "沪镍", "线材", "燃油","原油"]]
async FE_SH_Board(type, ...params) {
const FESHMap = {
ALL: 'C.SHFE',
ZN: 'C.F_SHFE_ZN',
PB: 'C.F_SHFE_PB',
BU: 'C.F_SHFE_BU',
AU: 'C.F_SHFE_AU',
RB: 'C.F_SHFE_RB',
AG: 'C.F_SHFE_AG',
HC: 'C.F_SHFE_HC',
AL: 'C.F_SHFE_AL',
SN: 'C.F_SHFE_SN',
RU: 'C.F_SHFE_RU',
CU: 'C.F_SHFE_CU',
NI: 'C.F_SHFE_NI',
WR: 'C.F_SHFE_WR',
FU: 'C.F_SHFE_FU',
INE: 'C.INE',
};
const datas = this.boradFn(FESHMap, ...params);
return datas(type, this.fecSty[1])
};
// 大商所 ["焦炭","聚丙烯","铁矿石","鸡蛋","豆油","豆一","棕榈油","纤维板","聚氯乙烯","聚乙烯","豆粕","豆二","焦煤","玉米","胶合板","玉米淀粉"]
async FE_DCE_Board(type, ...params) {
const FEDCEMap = {
"ALL": 'C.DCE',
"J": "C.F_DCE_J",
"PP": "C.F_DCE_PP",
"I": "C.F_DCE_I",
"JD": "C.F_DCE_JD",
"Y": "C.F_DCE_Y",
"A": "C.F_DCE_A",
"P": "C.F_DCE_P",
"FB": "C.F_DCE_FB",
"V": "C.F_DCE_V",
"L": "C.F_DCE_L",
"M": "C.F_DCE_M",
"B": "C.F_DCE_B",
"JM": "C.F_DCE_JM",
"C": "C.F_DCE_C",
"BB": "C.F_DCE_BB",
"CS": "C.F_DCE_CS"
};
const datas = this.boradFn(FEDCEMap, ...params);
return datas(type, this.fecSty[1])
};
// ["一号棉花","强麦","棉纱","苹果","早籼稻","硅铁","普麦","晚籼稻","甲醇","菜粕","玻璃","锰硅","PTA","动力煤","粳稻","白糖","菜籽","菜油"]
async FE_CZCE_Board(type, ...params) {
const CZCEMap = {
"ALL": "C.CZCE",
"CF": "C.F_CZCE_CF",
"WH": "C.F_CZCE_WH",
"CY": "C.F_CZCE_CY",
"AP": "C.F_CZCE_AP",
"ER": "C.F_CZCE_ER",
"SF": "C.F_CZCE_SF",
"WT": "C.F_CZCE_WT",
"LR": "C.F_CZCE_LR",
"MA": "C.F_CZCE_MA",
"RM": "C.F_CZCE_RM",
"FG": "C.F_CZCE_FG",
"SM": "C.F_CZCE_SM",
"TA": "C.F_CZCE_TA",
"ZC": "C.F_CZCE_ZC",
"JR": "C.F_CZCE_JR",
"SR": "C.F_CZCE_SR",
"RS": "C.F_CZCE_RS",
"OI": "C.F_CZCE_OI"
};
const datas = this.boradFn(CZCEMap, ...params);
return datas(type, this.fecSty[1])
};
// 中金所
async FE_CFFEX_Board(type, ...params) {
const FEMap = {
CFFEX_ALL: 'R._168|_169',
CFFEX_5: 'C._TF_FO',
CFFEX_10: 'C._T_FO',
CFFEX_IC_FO: 'C._IC_FO',
CFFEX_IF_FO: 'C._IF_FO',
CFFEX_IH_FO: 'C._IH_FO'
}
const datas = this.boradFn(FEMap, ...params);
return datas(type, this.fecSty[1])
};
/**
* 香港期货
*
* @param {*} type
* @param {*} params
* @returns
* @memberof boards
*/
// async FECHKBoard(type, ...params) {
// // [港交所,指数期货]
// const FEMap = {
// HKSTOCKF: 'C.HEX.HKSTOCKF',
// HKINDEXF: 'C.HEX.HKINDEXF',
// }
// const datas = this.boradFn(FEMap, ...params);
// return datas(type, this.fecSty[1])
// };
// ["玉米","豆油","超国债","迷你玉米","年美国债","小型道指","燕麦","小麦","十年美国债","大豆","豆粕","五年美国债","乙醇","稻谷","迷你大豆","迷你小麦","COMEX铜","COMEX白银","COMEX黄金","微型黄金","迷你黄金","迷你白银","重柴油","布伦特原油","天然气","场内锌","综合铜","伦铜现","LmeS_铜","场内铜","LmeS_锌","LmeS_铅","综合铅","综合镍","伦镍现","LmeS_镍","伦铅现","场内铝","伦铝现","场内镍","LmeS_铝","场内铅","综合铝","场内锡","综合锌","伦锌现","LmeS_锡","综合锡","伦锡现","LmeS合金","棕榈油","糖号","棉花","NYMEX汽油","NYMEX燃油","迷你原油","NYMEX钯金","NYMEX铂金","热轧钢卷","NYMEX原油","现货黄金","现货白银","现货铂金","A期指","号合成胶","cst燃油","号烟片胶","日铂金","日煤油","日黄金","日原油","日钯金","日白银","日汽油","日橡胶"]
async FE_GLO_Board(type, ...params) {
const FEGLOMap = {
"ZC": "C.UF_COBOT_ZC",
"ZL": "C.UF_COBOT_ZL",
"UL": "C.UF_COBOT_UL",
"XC": "C.UF_COBOT_XC",
"US": "C.UF_COBOT_US",
"YM": "C.UF_COBOT_YM",
"ZO": "C.UF_COBOT_ZO",
"ZW": "C.UF_COBOT_ZW",
"TY": "C.UF_COBOT_TY",
"ZS": "C.UF_COBOT_ZS",
"ZM": "C.UF_COBOT_ZM",
"FV": "C.UF_COBOT_FV",
"EH": "C.UF_COBOT_EH",
"ZR": "C.UF_COBOT_ZR",
"XK": "C.UF_COBOT_XK",
"XW": "C.UF_COBOT_XW",
"HG": "C.UF_COMEX_HG",
"SI": "C.UF_COMEX_SI",
"GC": "C.UF_COMEX_GC",
"MGC": "C.UF_COMEX_MGC",
"QO": "C.UF_COMEX_QO",
"QI": "C.UF_COMEX_QI",
"G": "C.UF_IPE_G",
"B": "C.UF_IPE_B",
"M": "C.UF_IPE_M",
"LZN": "C.UF_LME_LZN",
"LCP": "C.UF_LME_LCP",
"CPR": "C.UF_LME_CPR",
"LLD": "C.UF_LME_LLD",
"LNK": "C.UF_LME_LNK",
"NKR": "C.UF_LME_NKR",
"LDR": "C.UF_LME_LDR",
"LAL": "C.UF_LME_LAL",
"ALR": "C.UF_LME_ALR",
"LDD": "C.UF_LME_LDD",
"LTN": "C.UF_LME_LTN",
"ZHR": "C.UF_LME_ZHR",
"TNR": "C.UF_LME_TNR",
"LAA": "C.UF_LME_LAA",
"MPM": "C.UF_MDEX_MPM",
"SB": "C.UF_NYBOT_SB",
"CT": "C.UF_NYBOT_CT",
"RB": "C.UF_NYMEX_RB",
"HO": "C.UF_NYMEX_HO",
"QM": "C.UF_NYMEX_QM",
"PA": "C.UF_NYMEX_PA",
"PL": "C.UF_NYMEX_PL",
"HR": "C.UF_NYMEX_HR",
"CL": "C.UF_NYMEX_CL",
"AU": "C.UF_SGE_AU",
"AG": "C.UF_SGE_AG",
"PT": "C.UF_SGE_PT",
"CN": "C.UF_SGX_CN",
"TF": "C.UF_SGX_TF",
"FB": "C.UF_SGX_FB",
"RT": "C.UF_SGX_RT",
"JPL": "C.UF_TOCOM_JPL",
"JKE": "C.UF_TOCOM_JKE",
"JAU": "C.UF_TOCOM_JAU",
"JCO": "C.UF_TOCOM_JCO",
"JPA": "C.UF_TOCOM_JPA",
"JAG": "C.UF_TOCOM_JAG",
"JGL": "C.UF_TOCOM_JGL",
"JRU": "C.UF_TOCOM_JRU"
}
const datas = this.boradFn(FEGLOMap, ...params);
return datas(type, this.fecSty[1])
};
/**
* 金融期货GOL
* @param {*} type
* @param {*} params
*/
async FE_FINANCE_Board(type = 'ALL', ...params) {
const FINAMap = {
'ALL': 'R._ZJMF_Main_MonetaryFutures|_UMF_Main_MonetaryFutures'
};
const datas = this.boradFn(FINAMap, ...params);
return datas(type, this.fecSty[1])
};
/**
* 能源化工
*
* @param {string} [type='ALL']
* @param {*} params
* @returns promise
* @memberof futures
*/
async FE_ENERGY_Board(type = 'ALL', ...params) {
const ENERGYMap = {
'ALL': 'R._F_MAIN_ENERGY|_UF_MAIN_ENERGY'
};
const datas = this.boradFn(ENERGYMap, ...params);
return datas(type, this.fecSty[1])
};
/**
* 金属钢材
*
* @param {string} [type='ALL']
* @param {*} params
* @memberof futures
*/
async FE_METAL_Board(type = 'ALL', ...params) {
const METALMap = {
'ALL': 'R._F_MAIN_METAL|_UF_MAIN_METAL'
};
const datas = this.boradFn(METALMap, ...params);
return datas(type, this.fecSty[1])
};
//
/**
* 农产品食品原料
*
* @param {string} [type='ALL']
* @param {*} params
* @returns
* @memberof futures
*/
async FE_FARM_Board(type = 'ALL', ...params) {
const FARMMap = {
'ALL': 'R._F_MAIN_FARMPRODUCE|_UF_MAIN_FARMPRODUCE'
};
const datas = this.boradFn(FARMMap, ...params);
return datas(type, this.fecSty[1])
}
}
const client = new futures();?
// client.FE_DCE_Board('ALL', 1, 50).then(res => console.log(res));
client.FE_FARM_Board().then(res => console.log(res))股票类 stocks.js
/*************************************************************
*
*- Copyright (c), 股票数据API接口, 2018, lopo
*- FileName : stocks.js 股票
*- Author : 罗波 Version : 1.0 Date:2018-8-4
*- Descripttion : 股票 Node Express
*- Other : 数据来源-东方财富
*- JSVersion : ES6
*- Modules :
* 1.request
*
*----------------------------------------------------------
*- Class List :
* 1.getBoard
*- Function List :
* - tools
* 1. getCodeExchange
*
*- Class List :
* 1. stocks
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
'use strict'
const getBoard = require('../../utils/getBoard');
//
class stocks extends getBoard {
constructor() {
super();
this.stockSty = ['FCOIATC', 'CTBF', 'FPGBKI', 'CTF', 'FCABHL', 'FCRH']
};
/**
* 沪深个股集合
*
* - params
* @param {String|Array} code 股票代码 可以为 600000 | 600000,600001 | [600000,600001]
*
* - Desc 多用于个股自选接口用
**/
StockList(code) {
const opt = {
qs: {
cmd: this.setCodeCmd(code),
sty: this.stockSty[1]
}
}
return this.getDatas({...opt })
};
/**
* 沪深股市
*
* @param {String} type 指定字符串 1
*
* @returns Promise
*
* @memberof boards
*/
async HSBoard(type, ...params) {
// [沪深A股,上证A股,深证A股,新股,中小板,创业板,沪AB股比价,深AB股比价,B股,AB股比价,风险警示,两网及退市]
const boardMap = {
CHSA: 'C._A',
CHA: 'C.2',
CSA: 'C._SZAME',
CHSN: 'C.BL05011',
CHSZX: 'C.13',
CHSCY: 'C.80',
CHAB: 'C._ABPCSHZ',
CHSAB: 'C._ABPCSZZ',
CHSB: 'C._B',
CHSAB_AH: 'C._ABPCSHZ',
CHSW: 'C._AB_FXJS',
CHSE: 'R.__40|__42'
};
const datas = this.boradFn(boardMap, ...params);
return datas(type, type !== 'CHSAB_AH' ? this.stockSty[0] : this.stockSty[4])
};
/**
* 行业板块
* <type> <name> <desc> <default> <Must>
* @param {String} type 指定字符串 1
* @param {Number} p 当前页
* @param {Number} ps 每页数量 20
* @param {String} st 排序字段
* @param {Number} sr 排序方式 -1,1
*
* @returns promise
*
* @memberof boards
*/
async BKBoard(type, ...params) {
// [行业板块,地域板块,概念板块]
const BKMap = {
BKHY: 'C._BKHY',
BKDY: 'C._BKDY',
BKGN: 'C._BKGN'
};
const datas = this.boradFn(BKMap, ...params);
return datas(type, this.stockSty[2])
};
/**
* 国内指数
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async ZSBoard(type, ...params) {
// [上证指数,深证指数,指数成分]
const ZSMap = {
ZSSH: 'C.1',
ZSSZ: 'C.5',
ZSALL: 'C.IE.ALL'
};
const datas = this.boradFn(ZSMap, ...params);
return datas(type)
};
/**
* 港股通
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async HSGTBoard(type, ...params) {
// [沪股通,深股通,港股通(沪),港股通(深)]
const HSGTMap = {
SH_HK: 'C.BK07071',
SZ_HK: 'C.BK08041',
HK_SH: 'C.MK0144',
HK_SZ: 'C.MK0146',
};
const datas = this.boradFn(HSGTMap, ...params);
return datas(type)
};
/**
* 港股列表
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async HKBoard(type, ...params) {
// [所有港股,主力港股,创新港股,知名港股,港股蓝筹,港股红筹,港股红筹指数成分股,国企股,国企指数成分股,港股通成分股,HS综合大型,HS综合中型,AH比价,ADR,恒生指数]
// 比价参数 client.HKBoard('AH_COMP', 1, 5, '(AB/AH/HKD)').then(res => console.log(res))
const HKMap = {
HKALL: 'C._HKS',
HKMAIN: 'C.MK0107',
HKGEM: 'C.__28GEM',
HKWELL: 'C.MK0009',
HKBLUE: 'C.MK0104',
HKRED: 'C.MK0102',
HKRED_COMP: 'C.__28HSCIINDEX',
HKSTATE: 'C.__28HSCEI',
HKSTATE_COMP: 'C.__28HSCEIINDEX',
HK_COMP: 'C.MK0144',
HSI_LG_COMP: 'C.MK0141',
HSI_MD_COMP: 'C.MK0142',
AH_COMP: 'C._AHH',
HK_ADR: 'C._ADRA',
HS_ZS: 'R.HKI|HKIN|HS',
HK_WARRANTS: 'C._HKW'
};
const datas = this.boradFn(HKMap, ...params);
return datas(type, type !== 'AH_COMP' || type !== 'HS_ZS' || type !== 'HK_WARRANTS' ? this.stockSty[3] : this.stockSty[4])
};
/**
* 美股
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async USBoard(type, ...params) {
// [全部美股,[知名美股,[美科技股,金融,医药食品,媒体,汽车能源,制造零售]],[中国概念,中国互联网],美股指数]
const USMap = {
USALL: 'C._UNS',
USWELL: 'R.MK0216|MK0217|MK0218|MK0219|MK0220|MK0221',
USTECH: 'C.MK0216',
USFINA: 'C.MK0217',
USMEDI_FOOD: 'C.MK0218',
USMEDIA: 'C.MK0219',
USRAUTO_ENGNIN: 'C.MK0220',
USMADE_RETA: 'C.MK0221',
US_CN: 'R.MK0214|MK0212|MK0213|MK0202',
US_CNET: 'C.MK0202',
USZS: 'C._UI_MAP_USOA'
}
const datas = this.boradFn(USMap, ...params);
return datas(type)
};
/**
* 全球指数
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async GLOBoard(type, ...params) {
// [亚洲,美洲,欧洲,澳洲]
const GLOMap = {
ASIA: 'R.0000011,3990012,0003001,3990062,3990052,HSI5,HSCEI5,HSCCI5|_UI_MAP_ASIA',
AMERICA: 'C._UI_MAP_AME',
EURO: 'C._UI_MAP_EUR',
AUSTRALIA: 'C._UI_MAP_AUS'
}
const datas = this.boradFn(GLOMap, ...params);
return datas(type, this.stockSty[5])
};
};
module.exports = stocks;
const client = new stocks();
// client.getStockFullInfo('600803,600601').then(res => console.log(res));
// client.HSBoard('CHSA', 1, 2).then(res => console.log(res));
// client.HSBoard('CHSAB_AH', 1, 2, '(AB/AH/USD)').then(res => console.log(res));
// client.BKBoard('BKDY', 1, 5).then(res => console.log(res));
client.ZSBoard('ZSSH', 1, 15).then(res => console.log(res));
// client.HSGTBoard('HK_SH', 1, 5).then(res => console.log(res));
// client.HKBoard('AH_COMP', 1, 5, '(AB/AH/HKD)').then(res => console.log(res));
// client.HKBoard('HK_WARRANTS', 1, 5).then(res => console.log(res));
// client.ZSHSBoard('ALL', 1, 5).then(res => console.log(res));
// client.USBoard('USZS', 1, 5).then(res => console.log(res));
// client.HSBoard('CHSA', 1, 10000).then(res => console.log(res))
// client.FECHKBoard('HKSTOCKF').then(res => console.log(res)); 查看全部

以下代码仅仅用作学习交流,请勿用作其他用途


?基础类 getBoard
 /*************************************************************
*
*- Copyright (c), 股票数据API接口基础Class, 2018, lopo
*- FileName : default.js
*- Author : 罗波 Version : 1.0 Date:2018-8-4
*- Descripttion : 股票 Node Express
*- Other : 数据来源-东方财富
*- JSVersion : ES6
*- Modules :
* 1.request
* 2.cheerio
*
*----------------------------------------------------------
*
*- Function List :
* - tools
* 1. getCodeExchange
*
*- Class List :
* 1. boards
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
'use strict'
const request = require('request-promise-native');
const { getCodeExchange } = require('../utils/tools');

/**
* 基础配置
*
* @class getBoards
*/
class getBoards {
// 基础参数
constructor() {
// - stockSty : ['列表概览','个股详细']
this.reqOpt = {
uri: 'http://nufm.dfcfw.com/EM_Finance2014NumericApplication/JS.aspx',
qs: {
type: 'CT',
token: '64a483cbad8b666efa51677820e6b21c',
js: '({data:[(x)],totle:(tot)})',
'_': (new Date()).valueOf(),
}
};
// _rp_options.qs
this.request = request.defaults(this.reqOpt);
// - pageFn : p/当前页 ps/页数据量 st/排序字段 sr/排序方式
//* @param {Number} p 当前页
//* @param {Number} ps 每页数量 20
//* @param {String} st 排序字段
//* @param {Number} sr 排序方式 -1,1
this.pageOpt = (params) => {
return {
qs: {
p: params[0] || '1',
ps: params[1] || '20',
st: params[2] || '(ChangePercent)',
sr: params[3] || '-1'
}
}
};
/**
*
* @param {Objec} cmdMap 参数比对数组
* @param {Object} params page参数
* @param {String} type 参数
* @param {String} sty 展示方式
*
* @returns promise
*
* @memberof boards
*/
this.boradFn = (cmdMap, ...params) => (type, sty = this.stockSty[0]) => {
const boardMap = cmdMap;
if (!boardMap[type]) return { 'err': 'TypeError' };
const opt = this.pageOpt([...params]);
Object.assign(opt.qs, {
'cmd': boardMap[type],
'sty': sty
});
return this.getDatas({...opt })
};
};
/**
*格式化cmd配置亲求
*
* @param {String,Arrary} codes 股票代码格式化
* @returns String
* @memberof boards
*/
setCodeCmd(codes) {
if (codes.includes(',')) codes = codes.split(',');
return Array.isArray(codes) ? codes.reduce((a, b, i) => a += `${getCodeExchange(b)}${i<codes.length-1?',':''}`, '') : `${getCodeExchange(codes)}`
};
/**
*request 请求函数
*
* @param {Object} opt 请求配置
* @returns promise
* @memberof boards
*/
async getDatas(opt) {
try {
const datas = await this.request({...opt });
return await eval(datas);
} catch (error) {
return { 'err': 'NETError' }
}
};
}
module.exports = getBoards
辅助类 tool.js
const fixMoney = (money, fix = 2) => (money / (money.length < 8 ? 10000 : 100000000)).toFixed(fix) + (money.length < 8 ? '万' : '亿');
//
const timeout = async(ms) => {
await new Promise((resolve) => {
setTimeout(resolve, ms);
});
};
const getCodeExchange = (code, type = "stock") => {
let codes = code.substr(0, 3);
if (type === 'stock') return (codes === '000' || codes === '002') && `${code}2` || (codes === '600' || codes === '601' || codes === '603') && `${code}1` || `${code}2`
else return codes === '399' ? `${code}2` : `${code}1`
};
module.exports = {
fixMoney,
timeout,
getCodeExchange
}
基金 fund.js
 /*************************************************************
*
*- Copyright (c), 股票数据API接口 基金, 2018, lopo
*- FileName : default.js
*- Author : 罗波 Version : 1.0 Date:2018-8-4
*- Descripttion : 股票 Node Express
*- Other : 数据来源-东方财富
*- JSVersion : ES6
*
*----------------------------------------------------------
*- Class List :
* 1.getBoard
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
'use strict'
const getBoard = require('../../utils/getBoard');
//
class funds extends getBoard {
/**
* 封闭基金
*
* @param {string} [type='ALL']
* @param {*} params
* @returns
* @memberof futures
*/
// 封闭基金 ETF基金 LOF基金
async FUND_Board(type = 'ALL', ...params) {
const ENERGYMap = {
'CLOSE_END': 'C.__285002',
'ETF': 'C.__2850013',
'LOF': 'C.__2850014'
};
const datas = this.boradFn(ENERGYMap, ...params);
return datas(type, 'FCOIATC')
};

}
const client = new funds();?
// client.FE_DCE_Board('ALL', 1, 50).then(res => console.log(res));
client.FUND_Board('CLOSE_END', 1, 50).then(res => console.log(res))
期货类 futures.js
 /*************************************************************
*
*- Copyright (c), 股票数据API接口 期货, 2018, lopo
*- FileName : default.js
*- Author : 罗波 Version : 1.0 Date:2018-8-4
*- Descripttion : 股票 Node Express
*- Other : 数据来源-东方财富
*- JSVersion : ES6
*
*----------------------------------------------------------
*- Class List :
* 1.getBoard
*
*- Class List :
* 1. futures
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
'use strict'
const getBoard = require('../../utils/getBoard');
//
class futures extends getBoard {
constructor() {
super();
this.fecSty = ['FCHKEGL', 'FCFL4O']
};
/**
* 国内期货数据
*
* @param {*} type
* @param {*} params
* @returns
* @memberof boards
*/
//[上期所所有,["沪锌", "沪铅", "石油沥青", "沪金", "螺纹钢", "沪银", "热轧卷板", "沪铝", "沪锡", "橡胶", "沪铜", "沪镍", "线材", "燃油","原油"]]
async FE_SH_Board(type, ...params) {
const FESHMap = {
ALL: 'C.SHFE',
ZN: 'C.F_SHFE_ZN',
PB: 'C.F_SHFE_PB',
BU: 'C.F_SHFE_BU',
AU: 'C.F_SHFE_AU',
RB: 'C.F_SHFE_RB',
AG: 'C.F_SHFE_AG',
HC: 'C.F_SHFE_HC',
AL: 'C.F_SHFE_AL',
SN: 'C.F_SHFE_SN',
RU: 'C.F_SHFE_RU',
CU: 'C.F_SHFE_CU',
NI: 'C.F_SHFE_NI',
WR: 'C.F_SHFE_WR',
FU: 'C.F_SHFE_FU',
INE: 'C.INE',
};
const datas = this.boradFn(FESHMap, ...params);
return datas(type, this.fecSty[1])
};
// 大商所 ["焦炭","聚丙烯","铁矿石","鸡蛋","豆油","豆一","棕榈油","纤维板","聚氯乙烯","聚乙烯","豆粕","豆二","焦煤","玉米","胶合板","玉米淀粉"]
async FE_DCE_Board(type, ...params) {
const FEDCEMap = {
"ALL": 'C.DCE',
"J": "C.F_DCE_J",
"PP": "C.F_DCE_PP",
"I": "C.F_DCE_I",
"JD": "C.F_DCE_JD",
"Y": "C.F_DCE_Y",
"A": "C.F_DCE_A",
"P": "C.F_DCE_P",
"FB": "C.F_DCE_FB",
"V": "C.F_DCE_V",
"L": "C.F_DCE_L",
"M": "C.F_DCE_M",
"B": "C.F_DCE_B",
"JM": "C.F_DCE_JM",
"C": "C.F_DCE_C",
"BB": "C.F_DCE_BB",
"CS": "C.F_DCE_CS"
};
const datas = this.boradFn(FEDCEMap, ...params);
return datas(type, this.fecSty[1])
};
// ["一号棉花","强麦","棉纱","苹果","早籼稻","硅铁","普麦","晚籼稻","甲醇","菜粕","玻璃","锰硅","PTA","动力煤","粳稻","白糖","菜籽","菜油"]
async FE_CZCE_Board(type, ...params) {
const CZCEMap = {
"ALL": "C.CZCE",
"CF": "C.F_CZCE_CF",
"WH": "C.F_CZCE_WH",
"CY": "C.F_CZCE_CY",
"AP": "C.F_CZCE_AP",
"ER": "C.F_CZCE_ER",
"SF": "C.F_CZCE_SF",
"WT": "C.F_CZCE_WT",
"LR": "C.F_CZCE_LR",
"MA": "C.F_CZCE_MA",
"RM": "C.F_CZCE_RM",
"FG": "C.F_CZCE_FG",
"SM": "C.F_CZCE_SM",
"TA": "C.F_CZCE_TA",
"ZC": "C.F_CZCE_ZC",
"JR": "C.F_CZCE_JR",
"SR": "C.F_CZCE_SR",
"RS": "C.F_CZCE_RS",
"OI": "C.F_CZCE_OI"
};
const datas = this.boradFn(CZCEMap, ...params);
return datas(type, this.fecSty[1])
};
// 中金所
async FE_CFFEX_Board(type, ...params) {
const FEMap = {
CFFEX_ALL: 'R._168|_169',
CFFEX_5: 'C._TF_FO',
CFFEX_10: 'C._T_FO',
CFFEX_IC_FO: 'C._IC_FO',
CFFEX_IF_FO: 'C._IF_FO',
CFFEX_IH_FO: 'C._IH_FO'
}
const datas = this.boradFn(FEMap, ...params);
return datas(type, this.fecSty[1])
};
/**
* 香港期货
*
* @param {*} type
* @param {*} params
* @returns
* @memberof boards
*/
// async FECHKBoard(type, ...params) {
// // [港交所,指数期货]
// const FEMap = {
// HKSTOCKF: 'C.HEX.HKSTOCKF',
// HKINDEXF: 'C.HEX.HKINDEXF',
// }
// const datas = this.boradFn(FEMap, ...params);
// return datas(type, this.fecSty[1])
// };
// ["玉米","豆油","超国债","迷你玉米","年美国债","小型道指","燕麦","小麦","十年美国债","大豆","豆粕","五年美国债","乙醇","稻谷","迷你大豆","迷你小麦","COMEX铜","COMEX白银","COMEX黄金","微型黄金","迷你黄金","迷你白银","重柴油","布伦特原油","天然气","场内锌","综合铜","伦铜现","LmeS_铜","场内铜","LmeS_锌","LmeS_铅","综合铅","综合镍","伦镍现","LmeS_镍","伦铅现","场内铝","伦铝现","场内镍","LmeS_铝","场内铅","综合铝","场内锡","综合锌","伦锌现","LmeS_锡","综合锡","伦锡现","LmeS合金","棕榈油","糖号","棉花","NYMEX汽油","NYMEX燃油","迷你原油","NYMEX钯金","NYMEX铂金","热轧钢卷","NYMEX原油","现货黄金","现货白银","现货铂金","A期指","号合成胶","cst燃油","号烟片胶","日铂金","日煤油","日黄金","日原油","日钯金","日白银","日汽油","日橡胶"]
async FE_GLO_Board(type, ...params) {
const FEGLOMap = {
"ZC": "C.UF_COBOT_ZC",
"ZL": "C.UF_COBOT_ZL",
"UL": "C.UF_COBOT_UL",
"XC": "C.UF_COBOT_XC",
"US": "C.UF_COBOT_US",
"YM": "C.UF_COBOT_YM",
"ZO": "C.UF_COBOT_ZO",
"ZW": "C.UF_COBOT_ZW",
"TY": "C.UF_COBOT_TY",
"ZS": "C.UF_COBOT_ZS",
"ZM": "C.UF_COBOT_ZM",
"FV": "C.UF_COBOT_FV",
"EH": "C.UF_COBOT_EH",
"ZR": "C.UF_COBOT_ZR",
"XK": "C.UF_COBOT_XK",
"XW": "C.UF_COBOT_XW",
"HG": "C.UF_COMEX_HG",
"SI": "C.UF_COMEX_SI",
"GC": "C.UF_COMEX_GC",
"MGC": "C.UF_COMEX_MGC",
"QO": "C.UF_COMEX_QO",
"QI": "C.UF_COMEX_QI",
"G": "C.UF_IPE_G",
"B": "C.UF_IPE_B",
"M": "C.UF_IPE_M",
"LZN": "C.UF_LME_LZN",
"LCP": "C.UF_LME_LCP",
"CPR": "C.UF_LME_CPR",
"LLD": "C.UF_LME_LLD",
"LNK": "C.UF_LME_LNK",
"NKR": "C.UF_LME_NKR",
"LDR": "C.UF_LME_LDR",
"LAL": "C.UF_LME_LAL",
"ALR": "C.UF_LME_ALR",
"LDD": "C.UF_LME_LDD",
"LTN": "C.UF_LME_LTN",
"ZHR": "C.UF_LME_ZHR",
"TNR": "C.UF_LME_TNR",
"LAA": "C.UF_LME_LAA",
"MPM": "C.UF_MDEX_MPM",
"SB": "C.UF_NYBOT_SB",
"CT": "C.UF_NYBOT_CT",
"RB": "C.UF_NYMEX_RB",
"HO": "C.UF_NYMEX_HO",
"QM": "C.UF_NYMEX_QM",
"PA": "C.UF_NYMEX_PA",
"PL": "C.UF_NYMEX_PL",
"HR": "C.UF_NYMEX_HR",
"CL": "C.UF_NYMEX_CL",
"AU": "C.UF_SGE_AU",
"AG": "C.UF_SGE_AG",
"PT": "C.UF_SGE_PT",
"CN": "C.UF_SGX_CN",
"TF": "C.UF_SGX_TF",
"FB": "C.UF_SGX_FB",
"RT": "C.UF_SGX_RT",
"JPL": "C.UF_TOCOM_JPL",
"JKE": "C.UF_TOCOM_JKE",
"JAU": "C.UF_TOCOM_JAU",
"JCO": "C.UF_TOCOM_JCO",
"JPA": "C.UF_TOCOM_JPA",
"JAG": "C.UF_TOCOM_JAG",
"JGL": "C.UF_TOCOM_JGL",
"JRU": "C.UF_TOCOM_JRU"
}
const datas = this.boradFn(FEGLOMap, ...params);
return datas(type, this.fecSty[1])
};
/**
* 金融期货GOL
* @param {*} type
* @param {*} params
*/
async FE_FINANCE_Board(type = 'ALL', ...params) {
const FINAMap = {
'ALL': 'R._ZJMF_Main_MonetaryFutures|_UMF_Main_MonetaryFutures'
};
const datas = this.boradFn(FINAMap, ...params);
return datas(type, this.fecSty[1])
};
/**
* 能源化工
*
* @param {string} [type='ALL']
* @param {*} params
* @returns promise
* @memberof futures
*/
async FE_ENERGY_Board(type = 'ALL', ...params) {
const ENERGYMap = {
'ALL': 'R._F_MAIN_ENERGY|_UF_MAIN_ENERGY'
};
const datas = this.boradFn(ENERGYMap, ...params);
return datas(type, this.fecSty[1])
};
/**
* 金属钢材
*
* @param {string} [type='ALL']
* @param {*} params
* @memberof futures
*/
async FE_METAL_Board(type = 'ALL', ...params) {
const METALMap = {
'ALL': 'R._F_MAIN_METAL|_UF_MAIN_METAL'
};
const datas = this.boradFn(METALMap, ...params);
return datas(type, this.fecSty[1])
};
//
/**
* 农产品食品原料
*
* @param {string} [type='ALL']
* @param {*} params
* @returns
* @memberof futures
*/
async FE_FARM_Board(type = 'ALL', ...params) {
const FARMMap = {
'ALL': 'R._F_MAIN_FARMPRODUCE|_UF_MAIN_FARMPRODUCE'
};
const datas = this.boradFn(FARMMap, ...params);
return datas(type, this.fecSty[1])
}
}
const client = new futures();?
// client.FE_DCE_Board('ALL', 1, 50).then(res => console.log(res));
client.FE_FARM_Board().then(res => console.log(res))
股票类 stocks.js
 /*************************************************************
*
*- Copyright (c), 股票数据API接口, 2018, lopo
*- FileName : stocks.js 股票
*- Author : 罗波 Version : 1.0 Date:2018-8-4
*- Descripttion : 股票 Node Express
*- Other : 数据来源-东方财富
*- JSVersion : ES6
*- Modules :
* 1.request
*
*----------------------------------------------------------
*- Class List :
* 1.getBoard
*- Function List :
* - tools
* 1. getCodeExchange
*
*- Class List :
* 1. stocks
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
'use strict'
const getBoard = require('../../utils/getBoard');
//
class stocks extends getBoard {
constructor() {
super();
this.stockSty = ['FCOIATC', 'CTBF', 'FPGBKI', 'CTF', 'FCABHL', 'FCRH']
};
/**
* 沪深个股集合
*
* - params
* @param {String|Array} code 股票代码 可以为 600000 | 600000,600001 | [600000,600001]
*
* - Desc 多用于个股自选接口用
**/
StockList(code) {
const opt = {
qs: {
cmd: this.setCodeCmd(code),
sty: this.stockSty[1]
}
}
return this.getDatas({...opt })
};
/**
* 沪深股市
*
* @param {String} type 指定字符串 1
*
* @returns Promise
*
* @memberof boards
*/
async HSBoard(type, ...params) {
// [沪深A股,上证A股,深证A股,新股,中小板,创业板,沪AB股比价,深AB股比价,B股,AB股比价,风险警示,两网及退市]
const boardMap = {
CHSA: 'C._A',
CHA: 'C.2',
CSA: 'C._SZAME',
CHSN: 'C.BL05011',
CHSZX: 'C.13',
CHSCY: 'C.80',
CHAB: 'C._ABPCSHZ',
CHSAB: 'C._ABPCSZZ',
CHSB: 'C._B',
CHSAB_AH: 'C._ABPCSHZ',
CHSW: 'C._AB_FXJS',
CHSE: 'R.__40|__42'
};
const datas = this.boradFn(boardMap, ...params);
return datas(type, type !== 'CHSAB_AH' ? this.stockSty[0] : this.stockSty[4])
};
/**
* 行业板块
* <type> <name> <desc> <default> <Must>
* @param {String} type 指定字符串 1
* @param {Number} p 当前页
* @param {Number} ps 每页数量 20
* @param {String} st 排序字段
* @param {Number} sr 排序方式 -1,1
*
* @returns promise
*
* @memberof boards
*/
async BKBoard(type, ...params) {
// [行业板块,地域板块,概念板块]
const BKMap = {
BKHY: 'C._BKHY',
BKDY: 'C._BKDY',
BKGN: 'C._BKGN'
};
const datas = this.boradFn(BKMap, ...params);
return datas(type, this.stockSty[2])
};
/**
* 国内指数
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async ZSBoard(type, ...params) {
// [上证指数,深证指数,指数成分]
const ZSMap = {
ZSSH: 'C.1',
ZSSZ: 'C.5',
ZSALL: 'C.IE.ALL'
};
const datas = this.boradFn(ZSMap, ...params);
return datas(type)
};
/**
* 港股通
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async HSGTBoard(type, ...params) {
// [沪股通,深股通,港股通(沪),港股通(深)]
const HSGTMap = {
SH_HK: 'C.BK07071',
SZ_HK: 'C.BK08041',
HK_SH: 'C.MK0144',
HK_SZ: 'C.MK0146',
};
const datas = this.boradFn(HSGTMap, ...params);
return datas(type)
};
/**
* 港股列表
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async HKBoard(type, ...params) {
// [所有港股,主力港股,创新港股,知名港股,港股蓝筹,港股红筹,港股红筹指数成分股,国企股,国企指数成分股,港股通成分股,HS综合大型,HS综合中型,AH比价,ADR,恒生指数]
// 比价参数 client.HKBoard('AH_COMP', 1, 5, '(AB/AH/HKD)').then(res => console.log(res))
const HKMap = {
HKALL: 'C._HKS',
HKMAIN: 'C.MK0107',
HKGEM: 'C.__28GEM',
HKWELL: 'C.MK0009',
HKBLUE: 'C.MK0104',
HKRED: 'C.MK0102',
HKRED_COMP: 'C.__28HSCIINDEX',
HKSTATE: 'C.__28HSCEI',
HKSTATE_COMP: 'C.__28HSCEIINDEX',
HK_COMP: 'C.MK0144',
HSI_LG_COMP: 'C.MK0141',
HSI_MD_COMP: 'C.MK0142',
AH_COMP: 'C._AHH',
HK_ADR: 'C._ADRA',
HS_ZS: 'R.HKI|HKIN|HS',
HK_WARRANTS: 'C._HKW'
};
const datas = this.boradFn(HKMap, ...params);
return datas(type, type !== 'AH_COMP' || type !== 'HS_ZS' || type !== 'HK_WARRANTS' ? this.stockSty[3] : this.stockSty[4])
};
/**
* 美股
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async USBoard(type, ...params) {
// [全部美股,[知名美股,[美科技股,金融,医药食品,媒体,汽车能源,制造零售]],[中国概念,中国互联网],美股指数]
const USMap = {
USALL: 'C._UNS',
USWELL: 'R.MK0216|MK0217|MK0218|MK0219|MK0220|MK0221',
USTECH: 'C.MK0216',
USFINA: 'C.MK0217',
USMEDI_FOOD: 'C.MK0218',
USMEDIA: 'C.MK0219',
USRAUTO_ENGNIN: 'C.MK0220',
USMADE_RETA: 'C.MK0221',
US_CN: 'R.MK0214|MK0212|MK0213|MK0202',
US_CNET: 'C.MK0202',
USZS: 'C._UI_MAP_USOA'
}
const datas = this.boradFn(USMap, ...params);
return datas(type)
};
/**
* 全球指数
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async GLOBoard(type, ...params) {
// [亚洲,美洲,欧洲,澳洲]
const GLOMap = {
ASIA: 'R.0000011,3990012,0003001,3990062,3990052,HSI5,HSCEI5,HSCCI5|_UI_MAP_ASIA',
AMERICA: 'C._UI_MAP_AME',
EURO: 'C._UI_MAP_EUR',
AUSTRALIA: 'C._UI_MAP_AUS'
}
const datas = this.boradFn(GLOMap, ...params);
return datas(type, this.stockSty[5])
};
};
module.exports = stocks;
const client = new stocks();
// client.getStockFullInfo('600803,600601').then(res => console.log(res));
// client.HSBoard('CHSA', 1, 2).then(res => console.log(res));
// client.HSBoard('CHSAB_AH', 1, 2, '(AB/AH/USD)').then(res => console.log(res));
// client.BKBoard('BKDY', 1, 5).then(res => console.log(res));
client.ZSBoard('ZSSH', 1, 15).then(res => console.log(res));
// client.HSGTBoard('HK_SH', 1, 5).then(res => console.log(res));
// client.HKBoard('AH_COMP', 1, 5, '(AB/AH/HKD)').then(res => console.log(res));
// client.HKBoard('HK_WARRANTS', 1, 5).then(res => console.log(res));
// client.ZSHSBoard('ALL', 1, 5).then(res => console.log(res));
// client.USBoard('USZS', 1, 5).then(res => console.log(res));
// client.HSBoard('CHSA', 1, 10000).then(res => console.log(res))
// client.FECHKBoard('HKSTOCKF').then(res => console.log(res));

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

Nodejslopo1983 发表了文章 • 0 个评论 • 1543 次浏览 • 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 个评论 • 2268 次浏览 • 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;

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

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

?























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

?
设置.png


资金.png


客户.png


辅助资料.png


部门与员工.png

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

bs3.x dropdown 改写hover 触发

bootstrap3.xlopo1983 发表了文章 • 1 个评论 • 2391 次浏览 • 2016-01-06 17:09 • 来自相关话题

$('[data-toggle="dropdown"]').each(function() {
var $this = $(this),
$parent = $this.parent();
$this.off('click.dropdown.data-api');
$parent.hover(function() {
$this.dropdown('toggle');
});
}); 查看全部
				$('[data-toggle="dropdown"]').each(function() {
var $this = $(this),
$parent = $this.parent();
$this.off('click.dropdown.data-api');
$parent.hover(function() {
$this.dropdown('toggle');
});
});

如何限制让input只能输入字母(不分大小写)和中文和数字。

回复

javascript/jQuerycatchcat 回复了问题 • 1 人关注 • 1 个回复 • 5309 次浏览 • 2016-12-16 17:52 • 来自相关话题

如何清除input中的内容

回复

javascript/jQuerycatchcat 回复了问题 • 3 人关注 • 2 个回复 • 5612 次浏览 • 2016-12-16 16:44 • 来自相关话题

egg 处理提交的xml

Nodejslopo1983 发表了文章 • 0 个评论 • 2379 次浏览 • 2019-09-04 17:09 • 来自相关话题

最近在做企业微信相关的接口转发 发现回调是XML 数据格式 
egg默认是没有处理xml(目前在文档里面没找到.....)
看了下egg的源码 发现处理request 是用的bodyParser

config.default.js config.bodyParser = {
enable: true,
// @see https://github.com/hapijs/qs/b ... %23L8 for more options
queryString: {
arrayLimit: 100,
depth: 5,
parameterLimit: 1000,
},
enableTypes: ['json', 'form', 'text'],
extendTypes: {
text: ['text/xml', 'application/xml'],
},
};
同样的方法可以处理其他的任何格式 查看全部
最近在做企业微信相关的接口转发 发现回调是XML 数据格式 
egg默认是没有处理xml(目前在文档里面没找到.....)
看了下egg的源码 发现处理request 是用的bodyParser

config.default.js
    config.bodyParser = {
enable: true,
// @see https://github.com/hapijs/qs/b ... %23L8 for more options
queryString: {
arrayLimit: 100,
depth: 5,
parameterLimit: 1000,
},
enableTypes: ['json', 'form', 'text'],
extendTypes: {
text: ['text/xml', 'application/xml'],
},
};

同样的方法可以处理其他的任何格式


js偏函数

javascript/jQuerylopo1983 发表了文章 • 0 个评论 • 1604 次浏览 • 2019-06-10 09:50 • 来自相关话题

有一种函数叫偏函数( 左倾 ),其原理是将一些函数组合封装到一个函数中,调用时可以按顺序实现全部功能。使用时大多需要配合reduceRight
const compose = (...args) => x => args.reduceRight((result, cb) => cb(res), x); 查看全部

有一种函数叫偏函数( 左倾 ),其原理是将一些函数组合封装到一个函数中,调用时可以按顺序实现全部功能。使用时大多需要配合reduceRight


const compose = (...args) => x => args.reduceRight((result, cb) => cb(res), x);

egg.js+mongodb+openstack 公有云计费系统(三)OpenStack 对接 blcokStorage(2)

Nodejslopo1983 发表了文章 • 0 个评论 • 1386 次浏览 • 2019-03-15 11:27 • 来自相关话题

磁盘相关
控制器'use strict';

const Controller = require('egg').Controller;

class OPBlcokStorageVolumeController extends Controller {
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.QMFN = ctx.helper.queryParamFn;
this.DUFN = ctx.helper.deleteUndefined;
this.volumeMODEL = ctx.model.Openstack.BlcokStorage.Volumes;
this.volumeSERVICE = ctx.service.openstack.blcokStorage.volume;
};
/**
* @name 磁盘列表
*
* @param {String} detail 不需要参数
*
* ----------------------------------- 分页 -------------------------------------
*
* @param {String} limit 分页>>条数/页
* @param {String} marker markerID(上页最后条id)
*
* ----------------------------------- 排序 -------------------------------------
*
* @param {String} sort_dir a|b 排序方式 ['asc':default,'desc']
* @param {String} sort_key 排序字段 [status,name,created_at]
*
* @description query参数detail存在则显示实力配置列表详细信息,若不传仅显示概要列表
*
* @example GET /openstack/blcokstorage/volume?{detail}
*
*/
async index() {
const { ctx } = this;
const RESULT = await this.volumeMODEL.getUsers(this.auth.id, this.QMFN({...ctx.query }));
ctx.body = RESULT;
};
/**
* @name 获取单一磁盘
*
* @description
*
* @example GET /openstack/blcokstorage/volume/{id}
*
*/
async show() {
const ctx = this.ctx;
// const datas = await this.volumeSERVICE.show(ctx.params.id, this.auth.id);
// ctx.body = datas;
const RESULT = await this.volumeMODEL.getOne(this.auth.id, ctx.params.id, ctx.isAdmin())
ctx.body = RESULT;
};
/**
* @name 创建磁盘
*
* @description
*
* @example POST /openstack/blcokstorage/volume
*
* @param {String} size 大小
* @param {String} availability_zone Optional 可用区域
* @param {String} snapshot_id Optional 快照ID FN从快照创建
* @param {String} backup_id Optional 备份ID FN从备份创建
* @param {String} imageRef Optional 镜像ID FN从镜像创建
* @param {String} volume_type Optional 磁盘类型 默认HDD
*
*/
async create() {
const { ctx } = this;
const { availability_zone, snapshot_id, backup_id, imageRef, size = 5, volume_type = "hdd" } = ctx.request.body;
const BODYS = this.DUFN({ availability_zone, snapshot_id, backup_id, imageRef, size, volume_type })
const datas = await this.volumeMODEL.createOne(this.auth.id, BODYS)
ctx.body = datas
};
/**
* @name 调整磁盘
*
* @description
*
* @example POST /openstack/blcokstorage/volume
*
* @param {String} size 大小
* @param {String} availability_zone Optional 可用区域
* @param {String} snapshot_id Optional 快照ID FN从快照创建
* @param {String} backup_id Optional 备份ID FN从备份创建
* @param {String} imageRef Optional 镜像ID FN从镜像创建
* @param {String} volume_type Optional 磁盘类型 默认HDD
*
*/
async update() {
const ctx = this.ctx;
const BODYS = ctx.request.body;
const { action, name } = BODYS;
delete BODYS.action;
const datas = !!action ? await this.volumeSERVICE.action(this.auth.id, this.auth.ProjectID, ctx.params.id, action, BODYS) : await this.serverSERVICE.update(ctx.params.id, this.auth.id, this.DUFn({ name }));
ctx.body = datas
};
/**
* @name 删除磁盘
*
* @description
*
*/
async destroy() {
const ctx = this.ctx;
const DATAS = await this.volumeSERVICE.destroy(this.auth.id, this.auth.ProjectID, ctx.params.id);
ctx.body = DATAS
}

}

module.exports = OPBlcokStorageVolumeController;modelmodule.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const ctx = app.createAnonymousContext();
const {
helper,
service
} = app.createAnonymousContext();
const {
NumToStr
} = ctx.helper;
const OSblockStorageVolumeSchema = new Schema({
// 配置
config: {},
// 公司id
_comp: {
type: Schema.Types.ObjectId,
ref: 'Company',
required: true,
index: true
},
// 支付方式
payment: {
type: String,
default: 'prepay'
},
// 到期时间
endTime: {
type: Date
},
// 后台备注描述
_description: {

},
// 源信息
_metadata: {
type: String,
index: true
},
/*---------------------------------------------------*/
"status": {
type: 'String',
default: 'creating'
},
"migration_status": {},
"user_id": {},
"attachments": ,
"availability_zone": {},
"bootable": {},
"encrypted": {},
"created_at": {},
"description": {},
"updated_at": {},
"volume_type": {},
"name": {},
"replication_status": {},
"consistencygroup_id": {},
"source_volid": {},
"imageRef": {},
"backup_id": {},
"snapshot_id": {},
"multiattach": {},
"metadata": {},
"id": {
type: String,
index: true
},
"size": {},
"os-vol-host-attr:host": {},
"os-vol-tenant-attr:tenant_id": {}
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
OSblockStorageVolumeSchema.statics = {
getUsers: async function(_comp, queryMix) {
const serverSERVICE = ctx.service.openstack.server.servers;
const {
querys,
select,
sort,
limit,
marker
} = queryMix;
const withDetail = querys.detail === '' || querys.detail;
delete querys.detail;
let QUERTS = {};
for (const key in querys) {
QUERTS[key] = eval(`/${querys[key]}.*/i`)
}
const MODELS = (count = false) => this.find({
_comp,
...QUERTS,
...(marker && !count && {
'_id': {
"$lt": marker
}
}),
}, {
...select,
}, {
...!count && {
limit
},
'sort': {
_id: -1,
...sort
}
});
try {
const datas = await MODELS()
return {
data: {
result: datas,
limit,
marker: datas.length ? [...datas].pop()._id : '',
totalCount: await MODELS(true).count()
}
}
} catch (error) {
return error
}
},
/**
* @name 创建硬盘
* @param {*} _id
* @param {*} body
*/
createOne: async function(_id, bodys) {
try {
const {
endTime,
payment,
_metadata
} = bodys;
delete bodys._metadata;
delete bodys.endTime;
delete bodys.payment;
//
const DATAS = await service.openstack.blcokStorage.volume.create(_id, {
...bodys,
... {
"name": `volume-${Math.random().toString(36).slice(2, 8)}`
}
});
const {
id: _openstack_id
} = DATAS.data.volume;
!!DATAS.data.volume && await ctx.model.Schedule.Openstack.Volume.addOne({
_comp: _id,
_openstack_id,
type: 'volume',
active: 'creating',
_metadata
})
return !!DATAS.data.volume ? {
data: await this.create({
_comp: _id,
endTime,
payment,
_metadata,
...DATAS.data.volume
})
} : {
'code': 422,
'message': '创建失败'
};
} catch (error) {
return error
}
},
// 获取全部
getAll: async function(queryMix) {
const {
querys,
select,
pages,
sort,
dates
} = queryMix;
const MODELS = this.find({
'status': {
'$ne': 0
},
...querys,
// ...(!!pages && !!pages.marker && { '_id': { "$lt": pages.marker } }),
}, {
...select,
})
// .limit(!!pages ? pages.limit : 10)
.sort({
// '_id': -1,
'id': 1,
...sort
});
return {
data: {
result: await MODELS,
totalCount: await MODELS.count()
}
};
},
// 查看单个
getOne: async function(_comp, id, isAdmin) {
const MODELS = this.findOne({...isAdmin ? {
id
} : {
_comp,
id
}
});
try {
const RESULT = await MODELS;
return {
data: RESULT
}
} catch (error) {
return error
}
},
// 绑定到磁盘
/**
* @name 绑定到磁盘
* @param {*} _comp 用户UUID
* @param {*} id 磁盘ID
* @param {*} type 'bind'|'unbind' 绑定方式
*/
bindToECS: async function({
_comp,
id,
type = "bind"
} = {}, bodys) {
const MODEL = this.findOneAndUpdate({
_comp,
id
}, {
'attachments': type === 'bind' ? [bodys] : ,
'status': type === 'bind' ? 'in-use' : 'available'
});
try {
const datas = await MODEL;
} catch (error) {
return error
}
}
}
return mongoose.model('openstack_block_storage_volume', OSblockStorageVolumeSchema)
}service'use strict';
const ServerIndex = require('../index')
//
class VolumeService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.adminprojectID = this.config.openstack.projectID.default;
this.actions = ':8776/v3';
};
// 获取磁盘列表
async list(querys, _ucid, project_id, isAdmin) {
try {
const detail = Object.keys(querys).includes('detail');
delete querys.detail;
let SORT = {}
if (querys.sort_key) {
SORT = { 'sort': `${querys.sort_key}:${querys.sort_dir}` };
delete querys.sort_key;
delete querys.sort_dir;
}
const datas = await this.OSAJax(`${this.actions}/${!!isAdmin ? this.adminprojectID : project_id}/volumes${detail ? '/detail' : ''}`, {
...!isAdmin && { _ucid },
body: {
...querys,
...querys.sort_key && SORT,
...isAdmin && { 'all_tenants': true },
},
});
return {
data: {
result: datas.volumes.map(e => {
!!detail && e.attachments.length && (e.attachment = e.attachments[0].server_id)
delete e.links;
delete e.attachments;
return e
}).filter(e => e.status != 'deleting'),
totalCount: datas.volumes.length
}
};
} catch (error) {
return error
}
}
// 获取磁盘详情
async show(id, _ucid, _description) {
try {
const datas = await this.OSAJax(`${this.actions}/${await this.getProject(_ucid)}/volumes/${id}`, {
..._ucid !== 'admin' && { _ucid }
});
return _ucid === 'admin' ? { ...datas.volume, _description } : { data: datas.volume };
} catch (error) {
return error
}
}
// 创建磁盘
async create(_ucid, bodys, order = "false") {
const DATAS = this.OSAJax(`${this.actions}/${await this.getProject(_ucid)}/volumes`, {
body: {
'volume': {
... {
"availability_zone": null,
"source_volid": null,
"description": null,
"multiattach": false,
"snapshot_id": null,
"backup_id": null,
"imageRef": null,
"metadata": {},
"consistencygroup_id": null
},
...bodys
}
},
method: 'POST',
_ucid
});
try {
const datas = await DATAS
return !!order ? { data: datas } : datas
} catch (error) {
return error
}

}
// 删除磁盘
async destroy(_ucid, project_id, volume_id) {
try {
const datas = await this.OSAJax(`${this.actions}/${project_id}/volumes/${volume_id}`, {
_ucid,
method: 'DELETE',
full: true
});
return {
...datas.status === 404 && {
message: `${volume_id}不存在或已删除`
}
}
} catch (error) {

}
}
// 磁盘操作
// {'os-extend':'磁盘扩容','revert':'镜像回滚'}
async action(_ucid, project_id, volume_id, type, bodys) {
const isIn = (e) => ['os-extend', 'revert'].includes(e);
if (!isIn(type)) return { code: 422 };
const actionMAP = (e) => {
const OBJ = {};
OBJ[e] = !bodys ? null : bodys;
return OBJ;
}
try {
const DATAS = await this.OSAJax(`${this.actions}/${project_id}/volumes/${volume_id}/action`, {
body: actionMAP(type),
method: 'POST',
_ucid, full: true
});
return {
code: DATAS.status
}
} catch (error) {
return error
}
}
async update(_ucid, project_id, volume_id, bodys) {
try {
const DATAS = await this.OSAJax(`${this.actions}/${project_id}/volumes/${volume_id}`, {
_ucid,
bodys: {
...bodys
},
method: 'PUT'
});
return {
data: DATAS
}
} catch (error) {
return error
}
}
}
module.exports = VolumeService;
scheduleconst Subscription = require('egg').Subscription;

class getVolumeStatus extends Subscription {
constructor(ctx) {
super(ctx);
this.SERVICE = ctx.service.openstack.blcokStorage.volume;
this.MODELS = ctx.model.Schedule.Openstack.Volume;
this.volumeMODEL = ctx.model.Openstack.BlcokStorage.Volumes;
}
static get schedule() {
return {
interval: '10s',
type: 'worker',
};
}
async subscribe() {
const ctx = this.ctx;
const lists = await this.MODELS.getALL();
if (lists.length) {
const datas = await this.checkItem(lists);
}
}
async checkItem(result) {
// 调取记录
const checkFN = result.map(e => {
// 记录尝试次数
this.MODELS.retry(e._openstack_id)
return this.SERVICE.show(e._openstack_id, 'admin', e);
})
let DATAS = await Promise.all(checkFN);
// 检查ACTION
if (!!DATAS.length) {
const endOrder = DATAS.map(e => {
const { _comp, _openstack_id: id } = e._description;
delete e._description
delete e.links
if (e.status === 'available' || e.status === 'in-use') {
return this.volumeMODEL.findOneAndUpdate({ id }, { ...e });
}
})
DATAS = await Promise.all(endOrder);
};
// 清除已完成任务
if (DATAS.length) {
const clearSchedule = DATAS.map(e => {
if (!!e) {
const { id: _openstack_id } = e;
return this.MODELS.deleteOne({ '_openstack_id': _openstack_id })
}
})
DATAS = await Promise.all(clearSchedule);
}
}
}

module.exports = getVolumeStatus;
schedule model
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const ctx = app.createAnonymousContext();
const scheduleOSVolumeSchema = new Schema({
// 类型
type: {
type: String,
},
// 尝试次数
retry: {
type: Number,
dafault: 0
},
// 是否创建成功
status: {
type: Boolean,
default: false
},
// 操作方式 [BUILD:创建,DELETE:删除]
active: {
type: String,
},
// 数据ID
_openstack_id: {
type: String,
index: true
},
// 公司ID
_comp: {
type: String
},
// 其他配置参数
/**
* ecs:Number
*/
_description: {

},
// 其他配置元信息
_metadata: {

}
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
scheduleOSVolumeSchema.statics = {
getALL: async function (limit = 50) {
const MODEL = this.find().limit(limit);
try {
return await MODEL;
} catch (error) {
return error
}
},
addOne: async function (bodys) {
const MODEL = this.create({ ...bodys });
try {
const result = await MODEL;
return { code: !!result ? 201 : 404 }
} catch (error) {
return error
}
},
// destroyOne: async function (id) {
// const MODEL = this.deleteOne(id);
// try {
// const result = await MODEL
// return result
// } catch (error) {
// return error
// }
// },
retry: async function (_openstack_id) {
const MODEL = this.findOneAndUpdate({ _openstack_id }, {
'$inc': { retry: 1 }
});
try {
await MODEL
} catch (error) {
return error
}
}
}
return mongoose.model('schedule_openstack_volume', scheduleOSVolumeSchema);
} 查看全部
磁盘相关
控制器
'use strict';

const Controller = require('egg').Controller;

class OPBlcokStorageVolumeController extends Controller {
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.QMFN = ctx.helper.queryParamFn;
this.DUFN = ctx.helper.deleteUndefined;
this.volumeMODEL = ctx.model.Openstack.BlcokStorage.Volumes;
this.volumeSERVICE = ctx.service.openstack.blcokStorage.volume;
};
/**
* @name 磁盘列表
*
* @param {String} detail 不需要参数
*
* ----------------------------------- 分页 -------------------------------------
*
* @param {String} limit 分页>>条数/页
* @param {String} marker markerID(上页最后条id)
*
* ----------------------------------- 排序 -------------------------------------
*
* @param {String} sort_dir a|b 排序方式 ['asc':default,'desc']
* @param {String} sort_key 排序字段 [status,name,created_at]
*
* @description query参数detail存在则显示实力配置列表详细信息,若不传仅显示概要列表
*
* @example GET /openstack/blcokstorage/volume?{detail}
*
*/
async index() {
const { ctx } = this;
const RESULT = await this.volumeMODEL.getUsers(this.auth.id, this.QMFN({...ctx.query }));
ctx.body = RESULT;
};
/**
* @name 获取单一磁盘
*
* @description
*
* @example GET /openstack/blcokstorage/volume/{id}
*
*/
async show() {
const ctx = this.ctx;
// const datas = await this.volumeSERVICE.show(ctx.params.id, this.auth.id);
// ctx.body = datas;
const RESULT = await this.volumeMODEL.getOne(this.auth.id, ctx.params.id, ctx.isAdmin())
ctx.body = RESULT;
};
/**
* @name 创建磁盘
*
* @description
*
* @example POST /openstack/blcokstorage/volume
*
* @param {String} size 大小
* @param {String} availability_zone Optional 可用区域
* @param {String} snapshot_id Optional 快照ID FN从快照创建
* @param {String} backup_id Optional 备份ID FN从备份创建
* @param {String} imageRef Optional 镜像ID FN从镜像创建
* @param {String} volume_type Optional 磁盘类型 默认HDD
*
*/
async create() {
const { ctx } = this;
const { availability_zone, snapshot_id, backup_id, imageRef, size = 5, volume_type = "hdd" } = ctx.request.body;
const BODYS = this.DUFN({ availability_zone, snapshot_id, backup_id, imageRef, size, volume_type })
const datas = await this.volumeMODEL.createOne(this.auth.id, BODYS)
ctx.body = datas
};
/**
* @name 调整磁盘
*
* @description
*
* @example POST /openstack/blcokstorage/volume
*
* @param {String} size 大小
* @param {String} availability_zone Optional 可用区域
* @param {String} snapshot_id Optional 快照ID FN从快照创建
* @param {String} backup_id Optional 备份ID FN从备份创建
* @param {String} imageRef Optional 镜像ID FN从镜像创建
* @param {String} volume_type Optional 磁盘类型 默认HDD
*
*/
async update() {
const ctx = this.ctx;
const BODYS = ctx.request.body;
const { action, name } = BODYS;
delete BODYS.action;
const datas = !!action ? await this.volumeSERVICE.action(this.auth.id, this.auth.ProjectID, ctx.params.id, action, BODYS) : await this.serverSERVICE.update(ctx.params.id, this.auth.id, this.DUFn({ name }));
ctx.body = datas
};
/**
* @name 删除磁盘
*
* @description
*
*/
async destroy() {
const ctx = this.ctx;
const DATAS = await this.volumeSERVICE.destroy(this.auth.id, this.auth.ProjectID, ctx.params.id);
ctx.body = DATAS
}

}

module.exports = OPBlcokStorageVolumeController;
model
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const ctx = app.createAnonymousContext();
const {
helper,
service
} = app.createAnonymousContext();
const {
NumToStr
} = ctx.helper;
const OSblockStorageVolumeSchema = new Schema({
// 配置
config: {},
// 公司id
_comp: {
type: Schema.Types.ObjectId,
ref: 'Company',
required: true,
index: true
},
// 支付方式
payment: {
type: String,
default: 'prepay'
},
// 到期时间
endTime: {
type: Date
},
// 后台备注描述
_description: {

},
// 源信息
_metadata: {
type: String,
index: true
},
/*---------------------------------------------------*/
"status": {
type: 'String',
default: 'creating'
},
"migration_status": {},
"user_id": {},
"attachments": ,
"availability_zone": {},
"bootable": {},
"encrypted": {},
"created_at": {},
"description": {},
"updated_at": {},
"volume_type": {},
"name": {},
"replication_status": {},
"consistencygroup_id": {},
"source_volid": {},
"imageRef": {},
"backup_id": {},
"snapshot_id": {},
"multiattach": {},
"metadata": {},
"id": {
type: String,
index: true
},
"size": {},
"os-vol-host-attr:host": {},
"os-vol-tenant-attr:tenant_id": {}
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
OSblockStorageVolumeSchema.statics = {
getUsers: async function(_comp, queryMix) {
const serverSERVICE = ctx.service.openstack.server.servers;
const {
querys,
select,
sort,
limit,
marker
} = queryMix;
const withDetail = querys.detail === '' || querys.detail;
delete querys.detail;
let QUERTS = {};
for (const key in querys) {
QUERTS[key] = eval(`/${querys[key]}.*/i`)
}
const MODELS = (count = false) => this.find({
_comp,
...QUERTS,
...(marker && !count && {
'_id': {
"$lt": marker
}
}),
}, {
...select,
}, {
...!count && {
limit
},
'sort': {
_id: -1,
...sort
}
});
try {
const datas = await MODELS()
return {
data: {
result: datas,
limit,
marker: datas.length ? [...datas].pop()._id : '',
totalCount: await MODELS(true).count()
}
}
} catch (error) {
return error
}
},
/**
* @name 创建硬盘
* @param {*} _id
* @param {*} body
*/
createOne: async function(_id, bodys) {
try {
const {
endTime,
payment,
_metadata
} = bodys;
delete bodys._metadata;
delete bodys.endTime;
delete bodys.payment;
//
const DATAS = await service.openstack.blcokStorage.volume.create(_id, {
...bodys,
... {
"name": `volume-${Math.random().toString(36).slice(2, 8)}`
}
});
const {
id: _openstack_id
} = DATAS.data.volume;
!!DATAS.data.volume && await ctx.model.Schedule.Openstack.Volume.addOne({
_comp: _id,
_openstack_id,
type: 'volume',
active: 'creating',
_metadata
})
return !!DATAS.data.volume ? {
data: await this.create({
_comp: _id,
endTime,
payment,
_metadata,
...DATAS.data.volume
})
} : {
'code': 422,
'message': '创建失败'
};
} catch (error) {
return error
}
},
// 获取全部
getAll: async function(queryMix) {
const {
querys,
select,
pages,
sort,
dates
} = queryMix;
const MODELS = this.find({
'status': {
'$ne': 0
},
...querys,
// ...(!!pages && !!pages.marker && { '_id': { "$lt": pages.marker } }),
}, {
...select,
})
// .limit(!!pages ? pages.limit : 10)
.sort({
// '_id': -1,
'id': 1,
...sort
});
return {
data: {
result: await MODELS,
totalCount: await MODELS.count()
}
};
},
// 查看单个
getOne: async function(_comp, id, isAdmin) {
const MODELS = this.findOne({...isAdmin ? {
id
} : {
_comp,
id
}
});
try {
const RESULT = await MODELS;
return {
data: RESULT
}
} catch (error) {
return error
}
},
// 绑定到磁盘
/**
* @name 绑定到磁盘
* @param {*} _comp 用户UUID
* @param {*} id 磁盘ID
* @param {*} type 'bind'|'unbind' 绑定方式
*/
bindToECS: async function({
_comp,
id,
type = "bind"
} = {}, bodys) {
const MODEL = this.findOneAndUpdate({
_comp,
id
}, {
'attachments': type === 'bind' ? [bodys] : ,
'status': type === 'bind' ? 'in-use' : 'available'
});
try {
const datas = await MODEL;
} catch (error) {
return error
}
}
}
return mongoose.model('openstack_block_storage_volume', OSblockStorageVolumeSchema)
}
service
'use strict';
const ServerIndex = require('../index')
//
class VolumeService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.adminprojectID = this.config.openstack.projectID.default;
this.actions = ':8776/v3';
};
// 获取磁盘列表
async list(querys, _ucid, project_id, isAdmin) {
try {
const detail = Object.keys(querys).includes('detail');
delete querys.detail;
let SORT = {}
if (querys.sort_key) {
SORT = { 'sort': `${querys.sort_key}:${querys.sort_dir}` };
delete querys.sort_key;
delete querys.sort_dir;
}
const datas = await this.OSAJax(`${this.actions}/${!!isAdmin ? this.adminprojectID : project_id}/volumes${detail ? '/detail' : ''}`, {
...!isAdmin && { _ucid },
body: {
...querys,
...querys.sort_key && SORT,
...isAdmin && { 'all_tenants': true },
},
});
return {
data: {
result: datas.volumes.map(e => {
!!detail && e.attachments.length && (e.attachment = e.attachments[0].server_id)
delete e.links;
delete e.attachments;
return e
}).filter(e => e.status != 'deleting'),
totalCount: datas.volumes.length
}
};
} catch (error) {
return error
}
}
// 获取磁盘详情
async show(id, _ucid, _description) {
try {
const datas = await this.OSAJax(`${this.actions}/${await this.getProject(_ucid)}/volumes/${id}`, {
..._ucid !== 'admin' && { _ucid }
});
return _ucid === 'admin' ? { ...datas.volume, _description } : { data: datas.volume };
} catch (error) {
return error
}
}
// 创建磁盘
async create(_ucid, bodys, order = "false") {
const DATAS = this.OSAJax(`${this.actions}/${await this.getProject(_ucid)}/volumes`, {
body: {
'volume': {
... {
"availability_zone": null,
"source_volid": null,
"description": null,
"multiattach": false,
"snapshot_id": null,
"backup_id": null,
"imageRef": null,
"metadata": {},
"consistencygroup_id": null
},
...bodys
}
},
method: 'POST',
_ucid
});
try {
const datas = await DATAS
return !!order ? { data: datas } : datas
} catch (error) {
return error
}

}
// 删除磁盘
async destroy(_ucid, project_id, volume_id) {
try {
const datas = await this.OSAJax(`${this.actions}/${project_id}/volumes/${volume_id}`, {
_ucid,
method: 'DELETE',
full: true
});
return {
...datas.status === 404 && {
message: `${volume_id}不存在或已删除`
}
}
} catch (error) {

}
}
// 磁盘操作
// {'os-extend':'磁盘扩容','revert':'镜像回滚'}
async action(_ucid, project_id, volume_id, type, bodys) {
const isIn = (e) => ['os-extend', 'revert'].includes(e);
if (!isIn(type)) return { code: 422 };
const actionMAP = (e) => {
const OBJ = {};
OBJ[e] = !bodys ? null : bodys;
return OBJ;
}
try {
const DATAS = await this.OSAJax(`${this.actions}/${project_id}/volumes/${volume_id}/action`, {
body: actionMAP(type),
method: 'POST',
_ucid, full: true
});
return {
code: DATAS.status
}
} catch (error) {
return error
}
}
async update(_ucid, project_id, volume_id, bodys) {
try {
const DATAS = await this.OSAJax(`${this.actions}/${project_id}/volumes/${volume_id}`, {
_ucid,
bodys: {
...bodys
},
method: 'PUT'
});
return {
data: DATAS
}
} catch (error) {
return error
}
}
}
module.exports = VolumeService;

schedule
const Subscription = require('egg').Subscription;

class getVolumeStatus extends Subscription {
constructor(ctx) {
super(ctx);
this.SERVICE = ctx.service.openstack.blcokStorage.volume;
this.MODELS = ctx.model.Schedule.Openstack.Volume;
this.volumeMODEL = ctx.model.Openstack.BlcokStorage.Volumes;
}
static get schedule() {
return {
interval: '10s',
type: 'worker',
};
}
async subscribe() {
const ctx = this.ctx;
const lists = await this.MODELS.getALL();
if (lists.length) {
const datas = await this.checkItem(lists);
}
}
async checkItem(result) {
// 调取记录
const checkFN = result.map(e => {
// 记录尝试次数
this.MODELS.retry(e._openstack_id)
return this.SERVICE.show(e._openstack_id, 'admin', e);
})
let DATAS = await Promise.all(checkFN);
// 检查ACTION
if (!!DATAS.length) {
const endOrder = DATAS.map(e => {
const { _comp, _openstack_id: id } = e._description;
delete e._description
delete e.links
if (e.status === 'available' || e.status === 'in-use') {
return this.volumeMODEL.findOneAndUpdate({ id }, { ...e });
}
})
DATAS = await Promise.all(endOrder);
};
// 清除已完成任务
if (DATAS.length) {
const clearSchedule = DATAS.map(e => {
if (!!e) {
const { id: _openstack_id } = e;
return this.MODELS.deleteOne({ '_openstack_id': _openstack_id })
}
})
DATAS = await Promise.all(clearSchedule);
}
}
}

module.exports = getVolumeStatus;

schedule model
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const ctx = app.createAnonymousContext();
const scheduleOSVolumeSchema = new Schema({
// 类型
type: {
type: String,
},
// 尝试次数
retry: {
type: Number,
dafault: 0
},
// 是否创建成功
status: {
type: Boolean,
default: false
},
// 操作方式 [BUILD:创建,DELETE:删除]
active: {
type: String,
},
// 数据ID
_openstack_id: {
type: String,
index: true
},
// 公司ID
_comp: {
type: String
},
// 其他配置参数
/**
* ecs:Number
*/
_description: {

},
// 其他配置元信息
_metadata: {

}
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
scheduleOSVolumeSchema.statics = {
getALL: async function (limit = 50) {
const MODEL = this.find().limit(limit);
try {
return await MODEL;
} catch (error) {
return error
}
},
addOne: async function (bodys) {
const MODEL = this.create({ ...bodys });
try {
const result = await MODEL;
return { code: !!result ? 201 : 404 }
} catch (error) {
return error
}
},
// destroyOne: async function (id) {
// const MODEL = this.deleteOne(id);
// try {
// const result = await MODEL
// return result
// } catch (error) {
// return error
// }
// },
retry: async function (_openstack_id) {
const MODEL = this.findOneAndUpdate({ _openstack_id }, {
'$inc': { retry: 1 }
});
try {
await MODEL
} catch (error) {
return error
}
}
}
return mongoose.model('schedule_openstack_volume', scheduleOSVolumeSchema);
}

egg.js+mongodb+openstack 公有云计费系统(三)用户系统的搭建 (2)

Nodejslopo1983 发表了文章 • 0 个评论 • 1508 次浏览 • 2019-03-15 11:08 • 来自相关话题

A/SK 机制
?
控制器
?
/*************************************************************
*
*- Copyright (c) qiduo, 2018
*- FileName: AccesskeyController.js 安全认证 Accesskey接口
*- Author: 罗波 lopo1983@vip.qq.com
*- Version: 1.0
*- Date:2018-10-15
*- Descripttion:openstack geecp
*- Modules:
*
*- Extends
* - 1.egg_Controller
*----------------------------------------------------------
*
*- Function List :
*
*- StaticsList :
* - show 获取所有ak/sk
* - update 创建ak/sk 修改备注
* - destroy 删除ak/sk
*
*- History :
* <Author> <Date> <Desc>
*
**************************************************************/
'use strict';

const Controller = require('egg').Controller;

class AccesskeyController extends Controller {
/**
* @name constructor
*
* @param {*} ctx
* @param {Function} DuFn 清理空字段函数
* @param {Model} MODEL 公司表
* @param {Object} auth 用户JWT
*
*/
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.DUFn = ctx.helper.deleteUndefined;
this.MODEL = ctx.model.Account.Company;
}
/**
*
* @name 获取Accesskey列表
*
* @example
* - GET /user/accesskey/_id{ObjectId} HTTP/1.1
*
*/
async show() {
const ctx = this.ctx;
const result = await this.MODEL.getASK({ '_id': ctx.params.id });
ctx.body = result
}
/**
*
* @name 创建AK/SK|修改Info
*
* @param {String} info 必填 备注内容
* @param {ObjectId} id 必填 A/SK{ObjectId}
*
* @example
* - PUT /user/accesskey/user{ObjectId} HTTP/1.1 创建
* - PUT /user/accesskey/user{ObjectId} HTTP/1.1 修改
* body:{
* id:ak/sk{ObjectId}
* info:备注名称
* }
*
* @description info id 为必填参数
*
*/
async update() {
const ctx = this.ctx;
if (!!ctx.request.body.info) {
ctx.body = await this.MODEL.setInfo(ctx);
}
else {
ctx.body = await this.MODEL.createASK(ctx);
}
}
/**
*
* @name 删除AK/SK
*
* @param {ObjectId} id 必填 A/SK id
*
* @example
* PUT /user/accesskey/id{ObjectId} HTTP/1.1
* @description id 为必填参数
*
*/
async destroy() {
const ctx = this.ctx;
ctx.body = await this.MODEL.delASK(ctx);
}
/**
*
* @name ak/sk鉴权接口
*
* @param {ObjectId} uid 必填 用户uid
* @param {String} api_key 必填 accountKey
* @param {String} secret_key 必填 secretKey
*
* @example
* POST /user/authorization HTTP/1.1
*
* @description uid|api_key|secret_key 必填
*
*/
async authorization() {
const ctx = this.ctx;
const { uid, api_key, secret_key } = ctx.request.body;
ctx.body = await this.MODEL.apiToken({ uid, api_key, secret_key });
}
}

module.exports = AccesskeyController;

用户鉴权
控制器
'use strict';
const Controller = require('egg').Controller;
class ServiceJwtController extends Controller {
constructor(ctx) {
super(ctx);
this.MODELComp = ctx.model.Account.Company;
this.MODELAdmin = ctx.model.Account.Admin;
};
/**
* @name 用户token生成
*
* @param {action:string} 'api' 用于鉴权ak/sk生成token
* @param {String} compEmail 用户邮箱
* @param {String} compPhone 用户手机
* @param {String} compPassword 用户密码
*
* @description
*
* @example POST /user/sign/
*
* @return Object
*{
* "token": "",
* "info": {
* "_id": "5bcdd5e7f12ee030f44b6228",
* "compPhone": "13658157663",
* "compEmail": "64832897@qq.com",
* "compAuth": {
* "email": false,
* "phone": true
* },
* "compService": [],
* "updated": "2018-10-22T13:51:35.314Z",
* "compApi": []
* }
* }
*
**/
async sign() {
const ctx = this.ctx;
const {
body,
header
} = ctx.request;
try {
if (ctx.request.url.includes('admin')) {
ctx.body = await this.MODELAdmin.isLogin(ctx);
} else {
if (!['compEmail', 'compPhone'].map(e => Object.keys(body).includes(e)).reduce((a, b) => a + b)) {
ctx.body = {
code: 422,
message: '用户参数错误'
};
return
};
ctx.body = await this.MODELComp.isLogin(ctx);
}
} catch (error) {
console.log(error)
if (error.message === 'jwt expired') {
ctx.body = {
message: '用户鉴权失败,请重新登陆',
code: 401
}
} else {
ctx.body = {
message: !!error.errors ? `${error.errors.reduce((a, error) => { a.push(`${error.field} ${error.message}`); return a }, [])}` : error.message,
code: 422
}
}
}
}
}
module.exports = ServiceJwtControllerextend 扩展
简单用户划分
module.exports = {
isAdmin() {
console.log(this.url)
return this.state.user.group === 1 ? false : true;
},
};OpenStack 相关控制器
project
'use strict';

const Controller = require('egg').Controller;

class OPIdentityProjectsController extends Controller {
/**
* @name constructor
*
* @param {*} ctx
* @param {Function} DuFn 清理空字段函数
* @param {Object} auth 用户JWT
*
*/
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.DUFn = ctx.helper.deleteUndefined;
}
/**
* @name project列表
*
* @param {String} ctx.query 不需要参数
*
* @description query参数detail存在则显示实力配置列表详细信息,若不传仅显示概要列表
*
* @example GET /openstack/projects
*
*/
async index() {
const { ctx, service } = this;
const datas = await service.openstack.identity.projects.list(ctx.query);
ctx.body = datas;
}
/**
* @name 获取单一project信息
*
* @param {String} ctx.params.id 实例id
*
* @example GET /openstack/projects/{ctx.params.id}
*
*/
async show() {
const { ctx, service } = this;
const datas = await service.openstack.identity.projects.show(ctx.params.id);
ctx.body = datas;
}
/**
* @name 创建project
*
* @param {String} ctx.params.id 实例id
*
* @example
* - POST /openstack/projects/
*/
async create() {
const { ctx, service } = this;
const { name, is_domain, description, domain_id, enabled, parent_id, tags } = ctx.request.body;
const bodys = { name, is_domain, description, domain_id, enabled, parent_id, tags };
const datas = await service.openstack.identity.projects.create(this.DUFn(bodys))
ctx.body = datas;
}
/**
* @name 更新project
*
* @param {String} ctx.params.id 实例id
*
* @example
* - DELETE /openstack/projects/{ctx.params.id}
*/
async destroy() {
const { ctx, service } = this;
const datas = await service.openstack.identity.projects.destroy(ctx.params.id);
ctx.body = datas;
}
/**
* @name 删除project
*
* @param {String} ctx.params.id 实例id
*
* @example
* - PATCH /openstack/projects/{ctx.params.id}
*/
async update() {
const { ctx, service } = this;
const { name, is_domain, description, domain_id, enabled, parent_id, tags } = ctx.request.body;
const bodys = { name, is_domain, description, domain_id, enabled, parent_id, tags };
const datas = await service.openstack.identity.projects.update(ctx.params.id, this.DUFn(bodys))
ctx.body = datas;
}
/**
* @name 锁定不可操作项目
*
*/
checkID(idx) {
const ARR = ['c3513f27bbf24362b74d13e6afae2c37', '5d3b50c18fd44db4bc6abfdbbfcf6a3a', '5c7e341df8ff493c8ae7baf57c0129dd'];
return ARR.includes(idx);
}
}

module.exports = OPIdentityProjectsController;
region
'use strict';

const Controller = require('egg').Controller;

class identityUserRegionController extends Controller {
/**
* @name constructor
*
* @param {*} ctx
* @param {Function} DuFn 清理空字段函数
* @param {Object} auth 用户JWT
*
*/
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.DUFn = ctx.helper.deleteUndefined;
this.MODEL = ctx.model.Openstack.Identity.Region
this.TKMODEL = ctx.model.Openstack.Token
};
/**
*
* @name 用户列表
*
* @description
*
* @example GET /openstack/identity/region
*
*/
async index() {
const { ctx, service } = this;
const datas = await this.MODEL.getAll(this.auth.id, ctx.isAdmin());
ctx.body = datas;
}
}

module.exports = identityUserRegionController;user
'use strict';

const Controller = require('egg').Controller;

class identityUserController extends Controller {
/**
* @name constructor
*
* @param {*} ctx
* @param {Function} DuFn 清理空字段函数
* @param {Object} auth 用户JWT
*
*/
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.DUFn = ctx.helper.deleteUndefined;
this.MODEL = ctx.model.Openstack.Identity.User
this.TKMODEL = ctx.model.Openstack.Token
};
/**
*
* @name 用户列表
*
* @param {String} ctx.query.detail 不需要参数
*
* @description query参数detail存在则显示实力配置列表详细信息,若不传仅显示概要列表
*
* @example GET /openstack/user
*
*/
async index() {
const { ctx, service } = this;
ctx.body = await service.openstack.identity.users.list(ctx.query);
};
/**
* @name 获取单一用户信息
*
* @param {String} ctx.params.id 实例id
*
* @example GET /openstack/user/{ctx.params.id}
*
*/
async show() {
const { ctx, service } = this;
ctx.body = await service.openstack.identity.users.show(ctx.params.id);
// const result = await this.MODEL.getOne(this.auth.id);
// ctx.body = result;
};
/**
* @name 创建用户
*
* @param {String} ctx.params.id 实例id
*
* @example
* - POST /openstack/user/
*/
async create() {
const ctx = this.ctx;
const result = await this.MODEL.createUser(this.auth.id)
ctx.body = result;
// const { ctx, service } = this;
// const { default_project_id, domain_id, description, enabled = true, email, name, password } = ctx.request.body;
// const bodys = { default_project_id, domain_id, description, enabled, email, name, password };
// ctx.body = await service.openstack.identity.users.create(this.DUFn(bodys));
};
/**
*
* @name 测试内部用token
*
*/
async testToken() {
const { ctx, service } = this;
ctx.body = await this.TKMODEL.getToken(this.auth.id)
};
}

module.exports = identityUserController; 查看全部
A/SK 机制
?
控制器
?
/*************************************************************
*
*- Copyright (c) qiduo, 2018
*- FileName: AccesskeyController.js 安全认证 Accesskey接口
*- Author: 罗波 lopo1983@vip.qq.com
*- Version: 1.0
*- Date:2018-10-15
*- Descripttion:openstack geecp
*- Modules:
*
*- Extends
* - 1.egg_Controller
*----------------------------------------------------------
*
*- Function List :
*
*- StaticsList :
* - show 获取所有ak/sk
* - update 创建ak/sk 修改备注
* - destroy 删除ak/sk
*
*- History :
* <Author> <Date> <Desc>
*
**************************************************************/
'use strict';

const Controller = require('egg').Controller;

class AccesskeyController extends Controller {
/**
* @name constructor
*
* @param {*} ctx
* @param {Function} DuFn 清理空字段函数
* @param {Model} MODEL 公司表
* @param {Object} auth 用户JWT
*
*/
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.DUFn = ctx.helper.deleteUndefined;
this.MODEL = ctx.model.Account.Company;
}
/**
*
* @name 获取Accesskey列表
*
* @example
* - GET /user/accesskey/_id{ObjectId} HTTP/1.1
*
*/
async show() {
const ctx = this.ctx;
const result = await this.MODEL.getASK({ '_id': ctx.params.id });
ctx.body = result
}
/**
*
* @name 创建AK/SK|修改Info
*
* @param {String} info 必填 备注内容
* @param {ObjectId} id 必填 A/SK{ObjectId}
*
* @example
* - PUT /user/accesskey/user{ObjectId} HTTP/1.1 创建
* - PUT /user/accesskey/user{ObjectId} HTTP/1.1 修改
* body:{
* id:ak/sk{ObjectId}
* info:备注名称
* }
*
* @description info id 为必填参数
*
*/
async update() {
const ctx = this.ctx;
if (!!ctx.request.body.info) {
ctx.body = await this.MODEL.setInfo(ctx);
}
else {
ctx.body = await this.MODEL.createASK(ctx);
}
}
/**
*
* @name 删除AK/SK
*
* @param {ObjectId} id 必填 A/SK id
*
* @example
* PUT /user/accesskey/id{ObjectId} HTTP/1.1
* @description id 为必填参数
*
*/
async destroy() {
const ctx = this.ctx;
ctx.body = await this.MODEL.delASK(ctx);
}
/**
*
* @name ak/sk鉴权接口
*
* @param {ObjectId} uid 必填 用户uid
* @param {String} api_key 必填 accountKey
* @param {String} secret_key 必填 secretKey
*
* @example
* POST /user/authorization HTTP/1.1
*
* @description uid|api_key|secret_key 必填
*
*/
async authorization() {
const ctx = this.ctx;
const { uid, api_key, secret_key } = ctx.request.body;
ctx.body = await this.MODEL.apiToken({ uid, api_key, secret_key });
}
}

module.exports = AccesskeyController;

用户鉴权
控制器
'use strict';
const Controller = require('egg').Controller;
class ServiceJwtController extends Controller {
constructor(ctx) {
super(ctx);
this.MODELComp = ctx.model.Account.Company;
this.MODELAdmin = ctx.model.Account.Admin;
};
/**
* @name 用户token生成
*
* @param {action:string} 'api' 用于鉴权ak/sk生成token
* @param {String} compEmail 用户邮箱
* @param {String} compPhone 用户手机
* @param {String} compPassword 用户密码
*
* @description
*
* @example POST /user/sign/
*
* @return Object
*{
* "token": "",
* "info": {
* "_id": "5bcdd5e7f12ee030f44b6228",
* "compPhone": "13658157663",
* "compEmail": "64832897@qq.com",
* "compAuth": {
* "email": false,
* "phone": true
* },
* "compService": [],
* "updated": "2018-10-22T13:51:35.314Z",
* "compApi": []
* }
* }
*
**/
async sign() {
const ctx = this.ctx;
const {
body,
header
} = ctx.request;
try {
if (ctx.request.url.includes('admin')) {
ctx.body = await this.MODELAdmin.isLogin(ctx);
} else {
if (!['compEmail', 'compPhone'].map(e => Object.keys(body).includes(e)).reduce((a, b) => a + b)) {
ctx.body = {
code: 422,
message: '用户参数错误'
};
return
};
ctx.body = await this.MODELComp.isLogin(ctx);
}
} catch (error) {
console.log(error)
if (error.message === 'jwt expired') {
ctx.body = {
message: '用户鉴权失败,请重新登陆',
code: 401
}
} else {
ctx.body = {
message: !!error.errors ? `${error.errors.reduce((a, error) => { a.push(`${error.field} ${error.message}`); return a }, [])}` : error.message,
code: 422
}
}
}
}
}
module.exports = ServiceJwtController
extend 扩展
简单用户划分
module.exports = {
isAdmin() {
console.log(this.url)
return this.state.user.group === 1 ? false : true;
},
};
OpenStack 相关控制器
project
'use strict';

const Controller = require('egg').Controller;

class OPIdentityProjectsController extends Controller {
/**
* @name constructor
*
* @param {*} ctx
* @param {Function} DuFn 清理空字段函数
* @param {Object} auth 用户JWT
*
*/
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.DUFn = ctx.helper.deleteUndefined;
}
/**
* @name project列表
*
* @param {String} ctx.query 不需要参数
*
* @description query参数detail存在则显示实力配置列表详细信息,若不传仅显示概要列表
*
* @example GET /openstack/projects
*
*/
async index() {
const { ctx, service } = this;
const datas = await service.openstack.identity.projects.list(ctx.query);
ctx.body = datas;
}
/**
* @name 获取单一project信息
*
* @param {String} ctx.params.id 实例id
*
* @example GET /openstack/projects/{ctx.params.id}
*
*/
async show() {
const { ctx, service } = this;
const datas = await service.openstack.identity.projects.show(ctx.params.id);
ctx.body = datas;
}
/**
* @name 创建project
*
* @param {String} ctx.params.id 实例id
*
* @example
* - POST /openstack/projects/
*/
async create() {
const { ctx, service } = this;
const { name, is_domain, description, domain_id, enabled, parent_id, tags } = ctx.request.body;
const bodys = { name, is_domain, description, domain_id, enabled, parent_id, tags };
const datas = await service.openstack.identity.projects.create(this.DUFn(bodys))
ctx.body = datas;
}
/**
* @name 更新project
*
* @param {String} ctx.params.id 实例id
*
* @example
* - DELETE /openstack/projects/{ctx.params.id}
*/
async destroy() {
const { ctx, service } = this;
const datas = await service.openstack.identity.projects.destroy(ctx.params.id);
ctx.body = datas;
}
/**
* @name 删除project
*
* @param {String} ctx.params.id 实例id
*
* @example
* - PATCH /openstack/projects/{ctx.params.id}
*/
async update() {
const { ctx, service } = this;
const { name, is_domain, description, domain_id, enabled, parent_id, tags } = ctx.request.body;
const bodys = { name, is_domain, description, domain_id, enabled, parent_id, tags };
const datas = await service.openstack.identity.projects.update(ctx.params.id, this.DUFn(bodys))
ctx.body = datas;
}
/**
* @name 锁定不可操作项目
*
*/
checkID(idx) {
const ARR = ['c3513f27bbf24362b74d13e6afae2c37', '5d3b50c18fd44db4bc6abfdbbfcf6a3a', '5c7e341df8ff493c8ae7baf57c0129dd'];
return ARR.includes(idx);
}
}

module.exports = OPIdentityProjectsController;
region
'use strict';

const Controller = require('egg').Controller;

class identityUserRegionController extends Controller {
/**
* @name constructor
*
* @param {*} ctx
* @param {Function} DuFn 清理空字段函数
* @param {Object} auth 用户JWT
*
*/
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.DUFn = ctx.helper.deleteUndefined;
this.MODEL = ctx.model.Openstack.Identity.Region
this.TKMODEL = ctx.model.Openstack.Token
};
/**
*
* @name 用户列表
*
* @description
*
* @example GET /openstack/identity/region
*
*/
async index() {
const { ctx, service } = this;
const datas = await this.MODEL.getAll(this.auth.id, ctx.isAdmin());
ctx.body = datas;
}
}

module.exports = identityUserRegionController;
user
'use strict';

const Controller = require('egg').Controller;

class identityUserController extends Controller {
/**
* @name constructor
*
* @param {*} ctx
* @param {Function} DuFn 清理空字段函数
* @param {Object} auth 用户JWT
*
*/
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.DUFn = ctx.helper.deleteUndefined;
this.MODEL = ctx.model.Openstack.Identity.User
this.TKMODEL = ctx.model.Openstack.Token
};
/**
*
* @name 用户列表
*
* @param {String} ctx.query.detail 不需要参数
*
* @description query参数detail存在则显示实力配置列表详细信息,若不传仅显示概要列表
*
* @example GET /openstack/user
*
*/
async index() {
const { ctx, service } = this;
ctx.body = await service.openstack.identity.users.list(ctx.query);
};
/**
* @name 获取单一用户信息
*
* @param {String} ctx.params.id 实例id
*
* @example GET /openstack/user/{ctx.params.id}
*
*/
async show() {
const { ctx, service } = this;
ctx.body = await service.openstack.identity.users.show(ctx.params.id);
// const result = await this.MODEL.getOne(this.auth.id);
// ctx.body = result;
};
/**
* @name 创建用户
*
* @param {String} ctx.params.id 实例id
*
* @example
* - POST /openstack/user/
*/
async create() {
const ctx = this.ctx;
const result = await this.MODEL.createUser(this.auth.id)
ctx.body = result;
// const { ctx, service } = this;
// const { default_project_id, domain_id, description, enabled = true, email, name, password } = ctx.request.body;
// const bodys = { default_project_id, domain_id, description, enabled, email, name, password };
// ctx.body = await service.openstack.identity.users.create(this.DUFn(bodys));
};
/**
*
* @name 测试内部用token
*
*/
async testToken() {
const { ctx, service } = this;
ctx.body = await this.TKMODEL.getToken(this.auth.id)
};
}

module.exports = identityUserController;

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

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

?























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

?
设置.png


资金.png


客户.png


辅助资料.png


部门与员工.png

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

baidu sdk 鉴权处理(nodejs)

Nodejslopo1983 发表了文章 • 0 个评论 • 1394 次浏览 • 2019-01-18 17:25 • 来自相关话题

比较坑 但是还是爬出来了 参考代码
const { Auth } = require('bce-sdk-js');

const accessKey = 'aaaaaaaaaaaaa';
const secretKey = 'sssssssssssss';
const bdauth = new Auth(accessKey, secretKey);
const HEADER = {
'Host': 'bcc.bj.baidubce.com',
'Content-Type': 'application/json; charset=UTF-8',
'x-bce-date': new Date().toISOString().replace(/\.\d+Z$/, 'Z')
};
const Authorization = bdauth.generateAuthorization('GET', '/v2/image', { maxKeys: 1000, imageType: 'All' }, HEADER);当然 感觉baidu 已经放弃node了
可以自己尝试撸下
class authorization {
constructor() {
this.accessKey = ask.ak;
this.secretKey = ask.sk;
this.host = 'bcc.bj.baidubce.com';
this.utcTimestamp = new Date().toISOString().replace(/\.\d+Z$/, 'Z');
this.expireTime = 1800;
}
getAuthorization(method, uri, params, header, body) {
let authStringPrefix = `bce-auth-v1/${this.accessKey}/${this.utcTimestamp}/${this.expireTime}`;
let SigningKey = crypto.createHmac('sha256', this.secretKey.toString('ascii')).update(authStringPrefix).digest('hex');
let HEADER = {
'host': this.host,
'content-type': 'application%2Fjson%3B+charset%3Dutf-8',
'x-bce-date': this.utcTimestamp,
...header
};
const 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;
};
HEADER = OBJKeySort(HEADER)
let headerArr = [];
for (let name in HEADER) {
let val = HEADER[name];
headerArr.push(`${name.toLowerCase()}:${val}`);
}
let signedHeaders = Object.keys(HEADER).join(';');
let requestStr = `${method}\n${uri}\n${params?qs.stringify(params)+'\n':''}${headerArr.join('\n')}`;
let Signature = crypto.createHmac('sha256', SigningKey.toString('ascii')).update(requestStr).digest('hex');
return `${authStringPrefix}/${signedHeaders}/${Signature}`;
}
}
module.exports = authorization; 查看全部
比较坑 但是还是爬出来了 参考代码
const { Auth } = require('bce-sdk-js');

const accessKey = 'aaaaaaaaaaaaa';
const secretKey = 'sssssssssssss';
const bdauth = new Auth(accessKey, secretKey);
const HEADER = {
'Host': 'bcc.bj.baidubce.com',
'Content-Type': 'application/json; charset=UTF-8',
'x-bce-date': new Date().toISOString().replace(/\.\d+Z$/, 'Z')
};
const Authorization = bdauth.generateAuthorization('GET', '/v2/image', { maxKeys: 1000, imageType: 'All' }, HEADER);
当然 感觉baidu 已经放弃node了
可以自己尝试撸下
class authorization {
constructor() {
this.accessKey = ask.ak;
this.secretKey = ask.sk;
this.host = 'bcc.bj.baidubce.com';
this.utcTimestamp = new Date().toISOString().replace(/\.\d+Z$/, 'Z');
this.expireTime = 1800;
}
getAuthorization(method, uri, params, header, body) {
let authStringPrefix = `bce-auth-v1/${this.accessKey}/${this.utcTimestamp}/${this.expireTime}`;
let SigningKey = crypto.createHmac('sha256', this.secretKey.toString('ascii')).update(authStringPrefix).digest('hex');
let HEADER = {
'host': this.host,
'content-type': 'application%2Fjson%3B+charset%3Dutf-8',
'x-bce-date': this.utcTimestamp,
...header
};
const 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;
};
HEADER = OBJKeySort(HEADER)
let headerArr = [];
for (let name in HEADER) {
let val = HEADER[name];
headerArr.push(`${name.toLowerCase()}:${val}`);
}
let signedHeaders = Object.keys(HEADER).join(';');
let requestStr = `${method}\n${uri}\n${params?qs.stringify(params)+'\n':''}${headerArr.join('\n')}`;
let Signature = crypto.createHmac('sha256', SigningKey.toString('ascii')).update(requestStr).digest('hex');
return `${authStringPrefix}/${signedHeaders}/${Signature}`;
}
}
module.exports = authorization;

node爬取ui中国图片

Nodejslopo1983 发表了文章 • 0 个评论 • 1424 次浏览 • 2019-01-17 10:46 • 来自相关话题

以下代码仅供学习交流用,切勿做其他用途
/*************************************************************
*
*- Copyright (c), 2018, lopo qq:64832897
*- FileName : default.js
*- Author : 罗波 Version : 1.0 Date:2018-10-14
*- Descripttion : 图片image
*- Other : www.ui.cn
*- JSVersion : ES6
*- Modules :
* 1.request
* 2.cheerio
* 3.fs
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
"use strict"
const request = require('request-promise-native');
const cheerio = require('cheerio');
const fs = require('fs');
const timeout = async(ms) => {
await new Promise((resolve) => {
setTimeout(resolve, ms);
});
};
module.exports = {
async getImg() {
for (let j = 0; j < 2; j++) {
let DATA = Object.assign({}, BACKJSON);
let typeArr = ['all', 'main', 'edit', 'gener'];
const options = {
uri: 'http://www.ui.cn/list.html',
transform: (body) => cheerio.load(body),
qs: {
'r': 'main',
'p': j
},
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36'
}
};
let setDATA = async($) => {
let arrs = [];
await $('.imgloadinglater').each((i, e) => {
arrs.push({
url: $(e).parent().attr(),
src: $(e).data('original'),
name: $(e).data('original').split('/').pop(),
title: $(e).parents('.cover').next().find('.title').text().trim(),
})
});
for (const e of arrs) {
await request(e.src).pipe(fs.createWriteStream(`d:/ui/uichina/${e.name}`));
await timeout(1500);
}
console.log(arrs)
};
try {
const data = await request(options);
const json = setDATA(data);
} catch (error) {
console.log(error)
}
await timeout(15000);
}
}
} 查看全部

以下代码仅供学习交流用,切勿做其他用途


/*************************************************************
*
*- Copyright (c), 2018, lopo qq:64832897
*- FileName : default.js
*- Author : 罗波 Version : 1.0 Date:2018-10-14
*- Descripttion : 图片image
*- Other : www.ui.cn
*- JSVersion : ES6
*- Modules :
* 1.request
* 2.cheerio
* 3.fs
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
"use strict"
const request = require('request-promise-native');
const cheerio = require('cheerio');
const fs = require('fs');
const timeout = async(ms) => {
await new Promise((resolve) => {
setTimeout(resolve, ms);
});
};
module.exports = {
async getImg() {
for (let j = 0; j < 2; j++) {
let DATA = Object.assign({}, BACKJSON);
let typeArr = ['all', 'main', 'edit', 'gener'];
const options = {
uri: 'http://www.ui.cn/list.html',
transform: (body) => cheerio.load(body),
qs: {
'r': 'main',
'p': j
},
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36'
}
};
let setDATA = async($) => {
let arrs = [];
await $('.imgloadinglater').each((i, e) => {
arrs.push({
url: $(e).parent().attr(),
src: $(e).data('original'),
name: $(e).data('original').split('/').pop(),
title: $(e).parents('.cover').next().find('.title').text().trim(),
})
});
for (const e of arrs) {
await request(e.src).pipe(fs.createWriteStream(`d:/ui/uichina/${e.name}`));
await timeout(1500);
}
console.log(arrs)
};
try {
const data = await request(options);
const json = setDATA(data);
} catch (error) {
console.log(error)
}
await timeout(15000);
}
}
}

用nodejs编写股票数据爬虫

Nodejslopo1983 发表了文章 • 0 个评论 • 1770 次浏览 • 2019-01-16 15:52 • 来自相关话题

以下代码仅仅用作学习交流,请勿用作其他用途
?基础类 getBoard
/*************************************************************
*
*- Copyright (c), 股票数据API接口基础Class, 2018, lopo
*- FileName : default.js
*- Author : 罗波 Version : 1.0 Date:2018-8-4
*- Descripttion : 股票 Node Express
*- Other : 数据来源-东方财富
*- JSVersion : ES6
*- Modules :
* 1.request
* 2.cheerio
*
*----------------------------------------------------------
*
*- Function List :
* - tools
* 1. getCodeExchange
*
*- Class List :
* 1. boards
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
'use strict'
const request = require('request-promise-native');
const { getCodeExchange } = require('../utils/tools');

/**
* 基础配置
*
* @class getBoards
*/
class getBoards {
// 基础参数
constructor() {
// - stockSty : ['列表概览','个股详细']
this.reqOpt = {
uri: 'http://nufm.dfcfw.com/EM_Finance2014NumericApplication/JS.aspx',
qs: {
type: 'CT',
token: '64a483cbad8b666efa51677820e6b21c',
js: '({data:[(x)],totle:(tot)})',
'_': (new Date()).valueOf(),
}
};
// _rp_options.qs
this.request = request.defaults(this.reqOpt);
// - pageFn : p/当前页 ps/页数据量 st/排序字段 sr/排序方式
//* @param {Number} p 当前页
//* @param {Number} ps 每页数量 20
//* @param {String} st 排序字段
//* @param {Number} sr 排序方式 -1,1
this.pageOpt = (params) => {
return {
qs: {
p: params[0] || '1',
ps: params[1] || '20',
st: params[2] || '(ChangePercent)',
sr: params[3] || '-1'
}
}
};
/**
*
* @param {Objec} cmdMap 参数比对数组
* @param {Object} params page参数
* @param {String} type 参数
* @param {String} sty 展示方式
*
* @returns promise
*
* @memberof boards
*/
this.boradFn = (cmdMap, ...params) => (type, sty = this.stockSty[0]) => {
const boardMap = cmdMap;
if (!boardMap[type]) return { 'err': 'TypeError' };
const opt = this.pageOpt([...params]);
Object.assign(opt.qs, {
'cmd': boardMap[type],
'sty': sty
});
return this.getDatas({...opt })
};
};
/**
*格式化cmd配置亲求
*
* @param {String,Arrary} codes 股票代码格式化
* @returns String
* @memberof boards
*/
setCodeCmd(codes) {
if (codes.includes(',')) codes = codes.split(',');
return Array.isArray(codes) ? codes.reduce((a, b, i) => a += `${getCodeExchange(b)}${i<codes.length-1?',':''}`, '') : `${getCodeExchange(codes)}`
};
/**
*request 请求函数
*
* @param {Object} opt 请求配置
* @returns promise
* @memberof boards
*/
async getDatas(opt) {
try {
const datas = await this.request({...opt });
return await eval(datas);
} catch (error) {
return { 'err': 'NETError' }
}
};
}
module.exports = getBoards辅助类 tool.js
const fixMoney = (money, fix = 2) => (money / (money.length < 8 ? 10000 : 100000000)).toFixed(fix) + (money.length < 8 ? '万' : '亿');
//
const timeout = async(ms) => {
await new Promise((resolve) => {
setTimeout(resolve, ms);
});
};
const getCodeExchange = (code, type = "stock") => {
let codes = code.substr(0, 3);
if (type === 'stock') return (codes === '000' || codes === '002') && `${code}2` || (codes === '600' || codes === '601' || codes === '603') && `${code}1` || `${code}2`
else return codes === '399' ? `${code}2` : `${code}1`
};
module.exports = {
fixMoney,
timeout,
getCodeExchange
}基金 fund.js
/*************************************************************
*
*- Copyright (c), 股票数据API接口 基金, 2018, lopo
*- FileName : default.js
*- Author : 罗波 Version : 1.0 Date:2018-8-4
*- Descripttion : 股票 Node Express
*- Other : 数据来源-东方财富
*- JSVersion : ES6
*
*----------------------------------------------------------
*- Class List :
* 1.getBoard
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
'use strict'
const getBoard = require('../../utils/getBoard');
//
class funds extends getBoard {
/**
* 封闭基金
*
* @param {string} [type='ALL']
* @param {*} params
* @returns
* @memberof futures
*/
// 封闭基金 ETF基金 LOF基金
async FUND_Board(type = 'ALL', ...params) {
const ENERGYMap = {
'CLOSE_END': 'C.__285002',
'ETF': 'C.__2850013',
'LOF': 'C.__2850014'
};
const datas = this.boradFn(ENERGYMap, ...params);
return datas(type, 'FCOIATC')
};

}
const client = new funds();?
// client.FE_DCE_Board('ALL', 1, 50).then(res => console.log(res));
client.FUND_Board('CLOSE_END', 1, 50).then(res => console.log(res))期货类 futures.js
/*************************************************************
*
*- Copyright (c), 股票数据API接口 期货, 2018, lopo
*- FileName : default.js
*- Author : 罗波 Version : 1.0 Date:2018-8-4
*- Descripttion : 股票 Node Express
*- Other : 数据来源-东方财富
*- JSVersion : ES6
*
*----------------------------------------------------------
*- Class List :
* 1.getBoard
*
*- Class List :
* 1. futures
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
'use strict'
const getBoard = require('../../utils/getBoard');
//
class futures extends getBoard {
constructor() {
super();
this.fecSty = ['FCHKEGL', 'FCFL4O']
};
/**
* 国内期货数据
*
* @param {*} type
* @param {*} params
* @returns
* @memberof boards
*/
//[上期所所有,["沪锌", "沪铅", "石油沥青", "沪金", "螺纹钢", "沪银", "热轧卷板", "沪铝", "沪锡", "橡胶", "沪铜", "沪镍", "线材", "燃油","原油"]]
async FE_SH_Board(type, ...params) {
const FESHMap = {
ALL: 'C.SHFE',
ZN: 'C.F_SHFE_ZN',
PB: 'C.F_SHFE_PB',
BU: 'C.F_SHFE_BU',
AU: 'C.F_SHFE_AU',
RB: 'C.F_SHFE_RB',
AG: 'C.F_SHFE_AG',
HC: 'C.F_SHFE_HC',
AL: 'C.F_SHFE_AL',
SN: 'C.F_SHFE_SN',
RU: 'C.F_SHFE_RU',
CU: 'C.F_SHFE_CU',
NI: 'C.F_SHFE_NI',
WR: 'C.F_SHFE_WR',
FU: 'C.F_SHFE_FU',
INE: 'C.INE',
};
const datas = this.boradFn(FESHMap, ...params);
return datas(type, this.fecSty[1])
};
// 大商所 ["焦炭","聚丙烯","铁矿石","鸡蛋","豆油","豆一","棕榈油","纤维板","聚氯乙烯","聚乙烯","豆粕","豆二","焦煤","玉米","胶合板","玉米淀粉"]
async FE_DCE_Board(type, ...params) {
const FEDCEMap = {
"ALL": 'C.DCE',
"J": "C.F_DCE_J",
"PP": "C.F_DCE_PP",
"I": "C.F_DCE_I",
"JD": "C.F_DCE_JD",
"Y": "C.F_DCE_Y",
"A": "C.F_DCE_A",
"P": "C.F_DCE_P",
"FB": "C.F_DCE_FB",
"V": "C.F_DCE_V",
"L": "C.F_DCE_L",
"M": "C.F_DCE_M",
"B": "C.F_DCE_B",
"JM": "C.F_DCE_JM",
"C": "C.F_DCE_C",
"BB": "C.F_DCE_BB",
"CS": "C.F_DCE_CS"
};
const datas = this.boradFn(FEDCEMap, ...params);
return datas(type, this.fecSty[1])
};
// ["一号棉花","强麦","棉纱","苹果","早籼稻","硅铁","普麦","晚籼稻","甲醇","菜粕","玻璃","锰硅","PTA","动力煤","粳稻","白糖","菜籽","菜油"]
async FE_CZCE_Board(type, ...params) {
const CZCEMap = {
"ALL": "C.CZCE",
"CF": "C.F_CZCE_CF",
"WH": "C.F_CZCE_WH",
"CY": "C.F_CZCE_CY",
"AP": "C.F_CZCE_AP",
"ER": "C.F_CZCE_ER",
"SF": "C.F_CZCE_SF",
"WT": "C.F_CZCE_WT",
"LR": "C.F_CZCE_LR",
"MA": "C.F_CZCE_MA",
"RM": "C.F_CZCE_RM",
"FG": "C.F_CZCE_FG",
"SM": "C.F_CZCE_SM",
"TA": "C.F_CZCE_TA",
"ZC": "C.F_CZCE_ZC",
"JR": "C.F_CZCE_JR",
"SR": "C.F_CZCE_SR",
"RS": "C.F_CZCE_RS",
"OI": "C.F_CZCE_OI"
};
const datas = this.boradFn(CZCEMap, ...params);
return datas(type, this.fecSty[1])
};
// 中金所
async FE_CFFEX_Board(type, ...params) {
const FEMap = {
CFFEX_ALL: 'R._168|_169',
CFFEX_5: 'C._TF_FO',
CFFEX_10: 'C._T_FO',
CFFEX_IC_FO: 'C._IC_FO',
CFFEX_IF_FO: 'C._IF_FO',
CFFEX_IH_FO: 'C._IH_FO'
}
const datas = this.boradFn(FEMap, ...params);
return datas(type, this.fecSty[1])
};
/**
* 香港期货
*
* @param {*} type
* @param {*} params
* @returns
* @memberof boards
*/
// async FECHKBoard(type, ...params) {
// // [港交所,指数期货]
// const FEMap = {
// HKSTOCKF: 'C.HEX.HKSTOCKF',
// HKINDEXF: 'C.HEX.HKINDEXF',
// }
// const datas = this.boradFn(FEMap, ...params);
// return datas(type, this.fecSty[1])
// };
// ["玉米","豆油","超国债","迷你玉米","年美国债","小型道指","燕麦","小麦","十年美国债","大豆","豆粕","五年美国债","乙醇","稻谷","迷你大豆","迷你小麦","COMEX铜","COMEX白银","COMEX黄金","微型黄金","迷你黄金","迷你白银","重柴油","布伦特原油","天然气","场内锌","综合铜","伦铜现","LmeS_铜","场内铜","LmeS_锌","LmeS_铅","综合铅","综合镍","伦镍现","LmeS_镍","伦铅现","场内铝","伦铝现","场内镍","LmeS_铝","场内铅","综合铝","场内锡","综合锌","伦锌现","LmeS_锡","综合锡","伦锡现","LmeS合金","棕榈油","糖号","棉花","NYMEX汽油","NYMEX燃油","迷你原油","NYMEX钯金","NYMEX铂金","热轧钢卷","NYMEX原油","现货黄金","现货白银","现货铂金","A期指","号合成胶","cst燃油","号烟片胶","日铂金","日煤油","日黄金","日原油","日钯金","日白银","日汽油","日橡胶"]
async FE_GLO_Board(type, ...params) {
const FEGLOMap = {
"ZC": "C.UF_COBOT_ZC",
"ZL": "C.UF_COBOT_ZL",
"UL": "C.UF_COBOT_UL",
"XC": "C.UF_COBOT_XC",
"US": "C.UF_COBOT_US",
"YM": "C.UF_COBOT_YM",
"ZO": "C.UF_COBOT_ZO",
"ZW": "C.UF_COBOT_ZW",
"TY": "C.UF_COBOT_TY",
"ZS": "C.UF_COBOT_ZS",
"ZM": "C.UF_COBOT_ZM",
"FV": "C.UF_COBOT_FV",
"EH": "C.UF_COBOT_EH",
"ZR": "C.UF_COBOT_ZR",
"XK": "C.UF_COBOT_XK",
"XW": "C.UF_COBOT_XW",
"HG": "C.UF_COMEX_HG",
"SI": "C.UF_COMEX_SI",
"GC": "C.UF_COMEX_GC",
"MGC": "C.UF_COMEX_MGC",
"QO": "C.UF_COMEX_QO",
"QI": "C.UF_COMEX_QI",
"G": "C.UF_IPE_G",
"B": "C.UF_IPE_B",
"M": "C.UF_IPE_M",
"LZN": "C.UF_LME_LZN",
"LCP": "C.UF_LME_LCP",
"CPR": "C.UF_LME_CPR",
"LLD": "C.UF_LME_LLD",
"LNK": "C.UF_LME_LNK",
"NKR": "C.UF_LME_NKR",
"LDR": "C.UF_LME_LDR",
"LAL": "C.UF_LME_LAL",
"ALR": "C.UF_LME_ALR",
"LDD": "C.UF_LME_LDD",
"LTN": "C.UF_LME_LTN",
"ZHR": "C.UF_LME_ZHR",
"TNR": "C.UF_LME_TNR",
"LAA": "C.UF_LME_LAA",
"MPM": "C.UF_MDEX_MPM",
"SB": "C.UF_NYBOT_SB",
"CT": "C.UF_NYBOT_CT",
"RB": "C.UF_NYMEX_RB",
"HO": "C.UF_NYMEX_HO",
"QM": "C.UF_NYMEX_QM",
"PA": "C.UF_NYMEX_PA",
"PL": "C.UF_NYMEX_PL",
"HR": "C.UF_NYMEX_HR",
"CL": "C.UF_NYMEX_CL",
"AU": "C.UF_SGE_AU",
"AG": "C.UF_SGE_AG",
"PT": "C.UF_SGE_PT",
"CN": "C.UF_SGX_CN",
"TF": "C.UF_SGX_TF",
"FB": "C.UF_SGX_FB",
"RT": "C.UF_SGX_RT",
"JPL": "C.UF_TOCOM_JPL",
"JKE": "C.UF_TOCOM_JKE",
"JAU": "C.UF_TOCOM_JAU",
"JCO": "C.UF_TOCOM_JCO",
"JPA": "C.UF_TOCOM_JPA",
"JAG": "C.UF_TOCOM_JAG",
"JGL": "C.UF_TOCOM_JGL",
"JRU": "C.UF_TOCOM_JRU"
}
const datas = this.boradFn(FEGLOMap, ...params);
return datas(type, this.fecSty[1])
};
/**
* 金融期货GOL
* @param {*} type
* @param {*} params
*/
async FE_FINANCE_Board(type = 'ALL', ...params) {
const FINAMap = {
'ALL': 'R._ZJMF_Main_MonetaryFutures|_UMF_Main_MonetaryFutures'
};
const datas = this.boradFn(FINAMap, ...params);
return datas(type, this.fecSty[1])
};
/**
* 能源化工
*
* @param {string} [type='ALL']
* @param {*} params
* @returns promise
* @memberof futures
*/
async FE_ENERGY_Board(type = 'ALL', ...params) {
const ENERGYMap = {
'ALL': 'R._F_MAIN_ENERGY|_UF_MAIN_ENERGY'
};
const datas = this.boradFn(ENERGYMap, ...params);
return datas(type, this.fecSty[1])
};
/**
* 金属钢材
*
* @param {string} [type='ALL']
* @param {*} params
* @memberof futures
*/
async FE_METAL_Board(type = 'ALL', ...params) {
const METALMap = {
'ALL': 'R._F_MAIN_METAL|_UF_MAIN_METAL'
};
const datas = this.boradFn(METALMap, ...params);
return datas(type, this.fecSty[1])
};
//
/**
* 农产品食品原料
*
* @param {string} [type='ALL']
* @param {*} params
* @returns
* @memberof futures
*/
async FE_FARM_Board(type = 'ALL', ...params) {
const FARMMap = {
'ALL': 'R._F_MAIN_FARMPRODUCE|_UF_MAIN_FARMPRODUCE'
};
const datas = this.boradFn(FARMMap, ...params);
return datas(type, this.fecSty[1])
}
}
const client = new futures();?
// client.FE_DCE_Board('ALL', 1, 50).then(res => console.log(res));
client.FE_FARM_Board().then(res => console.log(res))股票类 stocks.js
/*************************************************************
*
*- Copyright (c), 股票数据API接口, 2018, lopo
*- FileName : stocks.js 股票
*- Author : 罗波 Version : 1.0 Date:2018-8-4
*- Descripttion : 股票 Node Express
*- Other : 数据来源-东方财富
*- JSVersion : ES6
*- Modules :
* 1.request
*
*----------------------------------------------------------
*- Class List :
* 1.getBoard
*- Function List :
* - tools
* 1. getCodeExchange
*
*- Class List :
* 1. stocks
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
'use strict'
const getBoard = require('../../utils/getBoard');
//
class stocks extends getBoard {
constructor() {
super();
this.stockSty = ['FCOIATC', 'CTBF', 'FPGBKI', 'CTF', 'FCABHL', 'FCRH']
};
/**
* 沪深个股集合
*
* - params
* @param {String|Array} code 股票代码 可以为 600000 | 600000,600001 | [600000,600001]
*
* - Desc 多用于个股自选接口用
**/
StockList(code) {
const opt = {
qs: {
cmd: this.setCodeCmd(code),
sty: this.stockSty[1]
}
}
return this.getDatas({...opt })
};
/**
* 沪深股市
*
* @param {String} type 指定字符串 1
*
* @returns Promise
*
* @memberof boards
*/
async HSBoard(type, ...params) {
// [沪深A股,上证A股,深证A股,新股,中小板,创业板,沪AB股比价,深AB股比价,B股,AB股比价,风险警示,两网及退市]
const boardMap = {
CHSA: 'C._A',
CHA: 'C.2',
CSA: 'C._SZAME',
CHSN: 'C.BL05011',
CHSZX: 'C.13',
CHSCY: 'C.80',
CHAB: 'C._ABPCSHZ',
CHSAB: 'C._ABPCSZZ',
CHSB: 'C._B',
CHSAB_AH: 'C._ABPCSHZ',
CHSW: 'C._AB_FXJS',
CHSE: 'R.__40|__42'
};
const datas = this.boradFn(boardMap, ...params);
return datas(type, type !== 'CHSAB_AH' ? this.stockSty[0] : this.stockSty[4])
};
/**
* 行业板块
* <type> <name> <desc> <default> <Must>
* @param {String} type 指定字符串 1
* @param {Number} p 当前页
* @param {Number} ps 每页数量 20
* @param {String} st 排序字段
* @param {Number} sr 排序方式 -1,1
*
* @returns promise
*
* @memberof boards
*/
async BKBoard(type, ...params) {
// [行业板块,地域板块,概念板块]
const BKMap = {
BKHY: 'C._BKHY',
BKDY: 'C._BKDY',
BKGN: 'C._BKGN'
};
const datas = this.boradFn(BKMap, ...params);
return datas(type, this.stockSty[2])
};
/**
* 国内指数
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async ZSBoard(type, ...params) {
// [上证指数,深证指数,指数成分]
const ZSMap = {
ZSSH: 'C.1',
ZSSZ: 'C.5',
ZSALL: 'C.IE.ALL'
};
const datas = this.boradFn(ZSMap, ...params);
return datas(type)
};
/**
* 港股通
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async HSGTBoard(type, ...params) {
// [沪股通,深股通,港股通(沪),港股通(深)]
const HSGTMap = {
SH_HK: 'C.BK07071',
SZ_HK: 'C.BK08041',
HK_SH: 'C.MK0144',
HK_SZ: 'C.MK0146',
};
const datas = this.boradFn(HSGTMap, ...params);
return datas(type)
};
/**
* 港股列表
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async HKBoard(type, ...params) {
// [所有港股,主力港股,创新港股,知名港股,港股蓝筹,港股红筹,港股红筹指数成分股,国企股,国企指数成分股,港股通成分股,HS综合大型,HS综合中型,AH比价,ADR,恒生指数]
// 比价参数 client.HKBoard('AH_COMP', 1, 5, '(AB/AH/HKD)').then(res => console.log(res))
const HKMap = {
HKALL: 'C._HKS',
HKMAIN: 'C.MK0107',
HKGEM: 'C.__28GEM',
HKWELL: 'C.MK0009',
HKBLUE: 'C.MK0104',
HKRED: 'C.MK0102',
HKRED_COMP: 'C.__28HSCIINDEX',
HKSTATE: 'C.__28HSCEI',
HKSTATE_COMP: 'C.__28HSCEIINDEX',
HK_COMP: 'C.MK0144',
HSI_LG_COMP: 'C.MK0141',
HSI_MD_COMP: 'C.MK0142',
AH_COMP: 'C._AHH',
HK_ADR: 'C._ADRA',
HS_ZS: 'R.HKI|HKIN|HS',
HK_WARRANTS: 'C._HKW'
};
const datas = this.boradFn(HKMap, ...params);
return datas(type, type !== 'AH_COMP' || type !== 'HS_ZS' || type !== 'HK_WARRANTS' ? this.stockSty[3] : this.stockSty[4])
};
/**
* 美股
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async USBoard(type, ...params) {
// [全部美股,[知名美股,[美科技股,金融,医药食品,媒体,汽车能源,制造零售]],[中国概念,中国互联网],美股指数]
const USMap = {
USALL: 'C._UNS',
USWELL: 'R.MK0216|MK0217|MK0218|MK0219|MK0220|MK0221',
USTECH: 'C.MK0216',
USFINA: 'C.MK0217',
USMEDI_FOOD: 'C.MK0218',
USMEDIA: 'C.MK0219',
USRAUTO_ENGNIN: 'C.MK0220',
USMADE_RETA: 'C.MK0221',
US_CN: 'R.MK0214|MK0212|MK0213|MK0202',
US_CNET: 'C.MK0202',
USZS: 'C._UI_MAP_USOA'
}
const datas = this.boradFn(USMap, ...params);
return datas(type)
};
/**
* 全球指数
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async GLOBoard(type, ...params) {
// [亚洲,美洲,欧洲,澳洲]
const GLOMap = {
ASIA: 'R.0000011,3990012,0003001,3990062,3990052,HSI5,HSCEI5,HSCCI5|_UI_MAP_ASIA',
AMERICA: 'C._UI_MAP_AME',
EURO: 'C._UI_MAP_EUR',
AUSTRALIA: 'C._UI_MAP_AUS'
}
const datas = this.boradFn(GLOMap, ...params);
return datas(type, this.stockSty[5])
};
};
module.exports = stocks;
const client = new stocks();
// client.getStockFullInfo('600803,600601').then(res => console.log(res));
// client.HSBoard('CHSA', 1, 2).then(res => console.log(res));
// client.HSBoard('CHSAB_AH', 1, 2, '(AB/AH/USD)').then(res => console.log(res));
// client.BKBoard('BKDY', 1, 5).then(res => console.log(res));
client.ZSBoard('ZSSH', 1, 15).then(res => console.log(res));
// client.HSGTBoard('HK_SH', 1, 5).then(res => console.log(res));
// client.HKBoard('AH_COMP', 1, 5, '(AB/AH/HKD)').then(res => console.log(res));
// client.HKBoard('HK_WARRANTS', 1, 5).then(res => console.log(res));
// client.ZSHSBoard('ALL', 1, 5).then(res => console.log(res));
// client.USBoard('USZS', 1, 5).then(res => console.log(res));
// client.HSBoard('CHSA', 1, 10000).then(res => console.log(res))
// client.FECHKBoard('HKSTOCKF').then(res => console.log(res)); 查看全部

以下代码仅仅用作学习交流,请勿用作其他用途


?基础类 getBoard
 /*************************************************************
*
*- Copyright (c), 股票数据API接口基础Class, 2018, lopo
*- FileName : default.js
*- Author : 罗波 Version : 1.0 Date:2018-8-4
*- Descripttion : 股票 Node Express
*- Other : 数据来源-东方财富
*- JSVersion : ES6
*- Modules :
* 1.request
* 2.cheerio
*
*----------------------------------------------------------
*
*- Function List :
* - tools
* 1. getCodeExchange
*
*- Class List :
* 1. boards
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
'use strict'
const request = require('request-promise-native');
const { getCodeExchange } = require('../utils/tools');

/**
* 基础配置
*
* @class getBoards
*/
class getBoards {
// 基础参数
constructor() {
// - stockSty : ['列表概览','个股详细']
this.reqOpt = {
uri: 'http://nufm.dfcfw.com/EM_Finance2014NumericApplication/JS.aspx',
qs: {
type: 'CT',
token: '64a483cbad8b666efa51677820e6b21c',
js: '({data:[(x)],totle:(tot)})',
'_': (new Date()).valueOf(),
}
};
// _rp_options.qs
this.request = request.defaults(this.reqOpt);
// - pageFn : p/当前页 ps/页数据量 st/排序字段 sr/排序方式
//* @param {Number} p 当前页
//* @param {Number} ps 每页数量 20
//* @param {String} st 排序字段
//* @param {Number} sr 排序方式 -1,1
this.pageOpt = (params) => {
return {
qs: {
p: params[0] || '1',
ps: params[1] || '20',
st: params[2] || '(ChangePercent)',
sr: params[3] || '-1'
}
}
};
/**
*
* @param {Objec} cmdMap 参数比对数组
* @param {Object} params page参数
* @param {String} type 参数
* @param {String} sty 展示方式
*
* @returns promise
*
* @memberof boards
*/
this.boradFn = (cmdMap, ...params) => (type, sty = this.stockSty[0]) => {
const boardMap = cmdMap;
if (!boardMap[type]) return { 'err': 'TypeError' };
const opt = this.pageOpt([...params]);
Object.assign(opt.qs, {
'cmd': boardMap[type],
'sty': sty
});
return this.getDatas({...opt })
};
};
/**
*格式化cmd配置亲求
*
* @param {String,Arrary} codes 股票代码格式化
* @returns String
* @memberof boards
*/
setCodeCmd(codes) {
if (codes.includes(',')) codes = codes.split(',');
return Array.isArray(codes) ? codes.reduce((a, b, i) => a += `${getCodeExchange(b)}${i<codes.length-1?',':''}`, '') : `${getCodeExchange(codes)}`
};
/**
*request 请求函数
*
* @param {Object} opt 请求配置
* @returns promise
* @memberof boards
*/
async getDatas(opt) {
try {
const datas = await this.request({...opt });
return await eval(datas);
} catch (error) {
return { 'err': 'NETError' }
}
};
}
module.exports = getBoards
辅助类 tool.js
const fixMoney = (money, fix = 2) => (money / (money.length < 8 ? 10000 : 100000000)).toFixed(fix) + (money.length < 8 ? '万' : '亿');
//
const timeout = async(ms) => {
await new Promise((resolve) => {
setTimeout(resolve, ms);
});
};
const getCodeExchange = (code, type = "stock") => {
let codes = code.substr(0, 3);
if (type === 'stock') return (codes === '000' || codes === '002') && `${code}2` || (codes === '600' || codes === '601' || codes === '603') && `${code}1` || `${code}2`
else return codes === '399' ? `${code}2` : `${code}1`
};
module.exports = {
fixMoney,
timeout,
getCodeExchange
}
基金 fund.js
 /*************************************************************
*
*- Copyright (c), 股票数据API接口 基金, 2018, lopo
*- FileName : default.js
*- Author : 罗波 Version : 1.0 Date:2018-8-4
*- Descripttion : 股票 Node Express
*- Other : 数据来源-东方财富
*- JSVersion : ES6
*
*----------------------------------------------------------
*- Class List :
* 1.getBoard
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
'use strict'
const getBoard = require('../../utils/getBoard');
//
class funds extends getBoard {
/**
* 封闭基金
*
* @param {string} [type='ALL']
* @param {*} params
* @returns
* @memberof futures
*/
// 封闭基金 ETF基金 LOF基金
async FUND_Board(type = 'ALL', ...params) {
const ENERGYMap = {
'CLOSE_END': 'C.__285002',
'ETF': 'C.__2850013',
'LOF': 'C.__2850014'
};
const datas = this.boradFn(ENERGYMap, ...params);
return datas(type, 'FCOIATC')
};

}
const client = new funds();?
// client.FE_DCE_Board('ALL', 1, 50).then(res => console.log(res));
client.FUND_Board('CLOSE_END', 1, 50).then(res => console.log(res))
期货类 futures.js
 /*************************************************************
*
*- Copyright (c), 股票数据API接口 期货, 2018, lopo
*- FileName : default.js
*- Author : 罗波 Version : 1.0 Date:2018-8-4
*- Descripttion : 股票 Node Express
*- Other : 数据来源-东方财富
*- JSVersion : ES6
*
*----------------------------------------------------------
*- Class List :
* 1.getBoard
*
*- Class List :
* 1. futures
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
'use strict'
const getBoard = require('../../utils/getBoard');
//
class futures extends getBoard {
constructor() {
super();
this.fecSty = ['FCHKEGL', 'FCFL4O']
};
/**
* 国内期货数据
*
* @param {*} type
* @param {*} params
* @returns
* @memberof boards
*/
//[上期所所有,["沪锌", "沪铅", "石油沥青", "沪金", "螺纹钢", "沪银", "热轧卷板", "沪铝", "沪锡", "橡胶", "沪铜", "沪镍", "线材", "燃油","原油"]]
async FE_SH_Board(type, ...params) {
const FESHMap = {
ALL: 'C.SHFE',
ZN: 'C.F_SHFE_ZN',
PB: 'C.F_SHFE_PB',
BU: 'C.F_SHFE_BU',
AU: 'C.F_SHFE_AU',
RB: 'C.F_SHFE_RB',
AG: 'C.F_SHFE_AG',
HC: 'C.F_SHFE_HC',
AL: 'C.F_SHFE_AL',
SN: 'C.F_SHFE_SN',
RU: 'C.F_SHFE_RU',
CU: 'C.F_SHFE_CU',
NI: 'C.F_SHFE_NI',
WR: 'C.F_SHFE_WR',
FU: 'C.F_SHFE_FU',
INE: 'C.INE',
};
const datas = this.boradFn(FESHMap, ...params);
return datas(type, this.fecSty[1])
};
// 大商所 ["焦炭","聚丙烯","铁矿石","鸡蛋","豆油","豆一","棕榈油","纤维板","聚氯乙烯","聚乙烯","豆粕","豆二","焦煤","玉米","胶合板","玉米淀粉"]
async FE_DCE_Board(type, ...params) {
const FEDCEMap = {
"ALL": 'C.DCE',
"J": "C.F_DCE_J",
"PP": "C.F_DCE_PP",
"I": "C.F_DCE_I",
"JD": "C.F_DCE_JD",
"Y": "C.F_DCE_Y",
"A": "C.F_DCE_A",
"P": "C.F_DCE_P",
"FB": "C.F_DCE_FB",
"V": "C.F_DCE_V",
"L": "C.F_DCE_L",
"M": "C.F_DCE_M",
"B": "C.F_DCE_B",
"JM": "C.F_DCE_JM",
"C": "C.F_DCE_C",
"BB": "C.F_DCE_BB",
"CS": "C.F_DCE_CS"
};
const datas = this.boradFn(FEDCEMap, ...params);
return datas(type, this.fecSty[1])
};
// ["一号棉花","强麦","棉纱","苹果","早籼稻","硅铁","普麦","晚籼稻","甲醇","菜粕","玻璃","锰硅","PTA","动力煤","粳稻","白糖","菜籽","菜油"]
async FE_CZCE_Board(type, ...params) {
const CZCEMap = {
"ALL": "C.CZCE",
"CF": "C.F_CZCE_CF",
"WH": "C.F_CZCE_WH",
"CY": "C.F_CZCE_CY",
"AP": "C.F_CZCE_AP",
"ER": "C.F_CZCE_ER",
"SF": "C.F_CZCE_SF",
"WT": "C.F_CZCE_WT",
"LR": "C.F_CZCE_LR",
"MA": "C.F_CZCE_MA",
"RM": "C.F_CZCE_RM",
"FG": "C.F_CZCE_FG",
"SM": "C.F_CZCE_SM",
"TA": "C.F_CZCE_TA",
"ZC": "C.F_CZCE_ZC",
"JR": "C.F_CZCE_JR",
"SR": "C.F_CZCE_SR",
"RS": "C.F_CZCE_RS",
"OI": "C.F_CZCE_OI"
};
const datas = this.boradFn(CZCEMap, ...params);
return datas(type, this.fecSty[1])
};
// 中金所
async FE_CFFEX_Board(type, ...params) {
const FEMap = {
CFFEX_ALL: 'R._168|_169',
CFFEX_5: 'C._TF_FO',
CFFEX_10: 'C._T_FO',
CFFEX_IC_FO: 'C._IC_FO',
CFFEX_IF_FO: 'C._IF_FO',
CFFEX_IH_FO: 'C._IH_FO'
}
const datas = this.boradFn(FEMap, ...params);
return datas(type, this.fecSty[1])
};
/**
* 香港期货
*
* @param {*} type
* @param {*} params
* @returns
* @memberof boards
*/
// async FECHKBoard(type, ...params) {
// // [港交所,指数期货]
// const FEMap = {
// HKSTOCKF: 'C.HEX.HKSTOCKF',
// HKINDEXF: 'C.HEX.HKINDEXF',
// }
// const datas = this.boradFn(FEMap, ...params);
// return datas(type, this.fecSty[1])
// };
// ["玉米","豆油","超国债","迷你玉米","年美国债","小型道指","燕麦","小麦","十年美国债","大豆","豆粕","五年美国债","乙醇","稻谷","迷你大豆","迷你小麦","COMEX铜","COMEX白银","COMEX黄金","微型黄金","迷你黄金","迷你白银","重柴油","布伦特原油","天然气","场内锌","综合铜","伦铜现","LmeS_铜","场内铜","LmeS_锌","LmeS_铅","综合铅","综合镍","伦镍现","LmeS_镍","伦铅现","场内铝","伦铝现","场内镍","LmeS_铝","场内铅","综合铝","场内锡","综合锌","伦锌现","LmeS_锡","综合锡","伦锡现","LmeS合金","棕榈油","糖号","棉花","NYMEX汽油","NYMEX燃油","迷你原油","NYMEX钯金","NYMEX铂金","热轧钢卷","NYMEX原油","现货黄金","现货白银","现货铂金","A期指","号合成胶","cst燃油","号烟片胶","日铂金","日煤油","日黄金","日原油","日钯金","日白银","日汽油","日橡胶"]
async FE_GLO_Board(type, ...params) {
const FEGLOMap = {
"ZC": "C.UF_COBOT_ZC",
"ZL": "C.UF_COBOT_ZL",
"UL": "C.UF_COBOT_UL",
"XC": "C.UF_COBOT_XC",
"US": "C.UF_COBOT_US",
"YM": "C.UF_COBOT_YM",
"ZO": "C.UF_COBOT_ZO",
"ZW": "C.UF_COBOT_ZW",
"TY": "C.UF_COBOT_TY",
"ZS": "C.UF_COBOT_ZS",
"ZM": "C.UF_COBOT_ZM",
"FV": "C.UF_COBOT_FV",
"EH": "C.UF_COBOT_EH",
"ZR": "C.UF_COBOT_ZR",
"XK": "C.UF_COBOT_XK",
"XW": "C.UF_COBOT_XW",
"HG": "C.UF_COMEX_HG",
"SI": "C.UF_COMEX_SI",
"GC": "C.UF_COMEX_GC",
"MGC": "C.UF_COMEX_MGC",
"QO": "C.UF_COMEX_QO",
"QI": "C.UF_COMEX_QI",
"G": "C.UF_IPE_G",
"B": "C.UF_IPE_B",
"M": "C.UF_IPE_M",
"LZN": "C.UF_LME_LZN",
"LCP": "C.UF_LME_LCP",
"CPR": "C.UF_LME_CPR",
"LLD": "C.UF_LME_LLD",
"LNK": "C.UF_LME_LNK",
"NKR": "C.UF_LME_NKR",
"LDR": "C.UF_LME_LDR",
"LAL": "C.UF_LME_LAL",
"ALR": "C.UF_LME_ALR",
"LDD": "C.UF_LME_LDD",
"LTN": "C.UF_LME_LTN",
"ZHR": "C.UF_LME_ZHR",
"TNR": "C.UF_LME_TNR",
"LAA": "C.UF_LME_LAA",
"MPM": "C.UF_MDEX_MPM",
"SB": "C.UF_NYBOT_SB",
"CT": "C.UF_NYBOT_CT",
"RB": "C.UF_NYMEX_RB",
"HO": "C.UF_NYMEX_HO",
"QM": "C.UF_NYMEX_QM",
"PA": "C.UF_NYMEX_PA",
"PL": "C.UF_NYMEX_PL",
"HR": "C.UF_NYMEX_HR",
"CL": "C.UF_NYMEX_CL",
"AU": "C.UF_SGE_AU",
"AG": "C.UF_SGE_AG",
"PT": "C.UF_SGE_PT",
"CN": "C.UF_SGX_CN",
"TF": "C.UF_SGX_TF",
"FB": "C.UF_SGX_FB",
"RT": "C.UF_SGX_RT",
"JPL": "C.UF_TOCOM_JPL",
"JKE": "C.UF_TOCOM_JKE",
"JAU": "C.UF_TOCOM_JAU",
"JCO": "C.UF_TOCOM_JCO",
"JPA": "C.UF_TOCOM_JPA",
"JAG": "C.UF_TOCOM_JAG",
"JGL": "C.UF_TOCOM_JGL",
"JRU": "C.UF_TOCOM_JRU"
}
const datas = this.boradFn(FEGLOMap, ...params);
return datas(type, this.fecSty[1])
};
/**
* 金融期货GOL
* @param {*} type
* @param {*} params
*/
async FE_FINANCE_Board(type = 'ALL', ...params) {
const FINAMap = {
'ALL': 'R._ZJMF_Main_MonetaryFutures|_UMF_Main_MonetaryFutures'
};
const datas = this.boradFn(FINAMap, ...params);
return datas(type, this.fecSty[1])
};
/**
* 能源化工
*
* @param {string} [type='ALL']
* @param {*} params
* @returns promise
* @memberof futures
*/
async FE_ENERGY_Board(type = 'ALL', ...params) {
const ENERGYMap = {
'ALL': 'R._F_MAIN_ENERGY|_UF_MAIN_ENERGY'
};
const datas = this.boradFn(ENERGYMap, ...params);
return datas(type, this.fecSty[1])
};
/**
* 金属钢材
*
* @param {string} [type='ALL']
* @param {*} params
* @memberof futures
*/
async FE_METAL_Board(type = 'ALL', ...params) {
const METALMap = {
'ALL': 'R._F_MAIN_METAL|_UF_MAIN_METAL'
};
const datas = this.boradFn(METALMap, ...params);
return datas(type, this.fecSty[1])
};
//
/**
* 农产品食品原料
*
* @param {string} [type='ALL']
* @param {*} params
* @returns
* @memberof futures
*/
async FE_FARM_Board(type = 'ALL', ...params) {
const FARMMap = {
'ALL': 'R._F_MAIN_FARMPRODUCE|_UF_MAIN_FARMPRODUCE'
};
const datas = this.boradFn(FARMMap, ...params);
return datas(type, this.fecSty[1])
}
}
const client = new futures();?
// client.FE_DCE_Board('ALL', 1, 50).then(res => console.log(res));
client.FE_FARM_Board().then(res => console.log(res))
股票类 stocks.js
 /*************************************************************
*
*- Copyright (c), 股票数据API接口, 2018, lopo
*- FileName : stocks.js 股票
*- Author : 罗波 Version : 1.0 Date:2018-8-4
*- Descripttion : 股票 Node Express
*- Other : 数据来源-东方财富
*- JSVersion : ES6
*- Modules :
* 1.request
*
*----------------------------------------------------------
*- Class List :
* 1.getBoard
*- Function List :
* - tools
* 1. getCodeExchange
*
*- Class List :
* 1. stocks
*
*- History :
* <Author> <Date> <Desc> <Modi>
*
**************************************************************/
'use strict'
const getBoard = require('../../utils/getBoard');
//
class stocks extends getBoard {
constructor() {
super();
this.stockSty = ['FCOIATC', 'CTBF', 'FPGBKI', 'CTF', 'FCABHL', 'FCRH']
};
/**
* 沪深个股集合
*
* - params
* @param {String|Array} code 股票代码 可以为 600000 | 600000,600001 | [600000,600001]
*
* - Desc 多用于个股自选接口用
**/
StockList(code) {
const opt = {
qs: {
cmd: this.setCodeCmd(code),
sty: this.stockSty[1]
}
}
return this.getDatas({...opt })
};
/**
* 沪深股市
*
* @param {String} type 指定字符串 1
*
* @returns Promise
*
* @memberof boards
*/
async HSBoard(type, ...params) {
// [沪深A股,上证A股,深证A股,新股,中小板,创业板,沪AB股比价,深AB股比价,B股,AB股比价,风险警示,两网及退市]
const boardMap = {
CHSA: 'C._A',
CHA: 'C.2',
CSA: 'C._SZAME',
CHSN: 'C.BL05011',
CHSZX: 'C.13',
CHSCY: 'C.80',
CHAB: 'C._ABPCSHZ',
CHSAB: 'C._ABPCSZZ',
CHSB: 'C._B',
CHSAB_AH: 'C._ABPCSHZ',
CHSW: 'C._AB_FXJS',
CHSE: 'R.__40|__42'
};
const datas = this.boradFn(boardMap, ...params);
return datas(type, type !== 'CHSAB_AH' ? this.stockSty[0] : this.stockSty[4])
};
/**
* 行业板块
* <type> <name> <desc> <default> <Must>
* @param {String} type 指定字符串 1
* @param {Number} p 当前页
* @param {Number} ps 每页数量 20
* @param {String} st 排序字段
* @param {Number} sr 排序方式 -1,1
*
* @returns promise
*
* @memberof boards
*/
async BKBoard(type, ...params) {
// [行业板块,地域板块,概念板块]
const BKMap = {
BKHY: 'C._BKHY',
BKDY: 'C._BKDY',
BKGN: 'C._BKGN'
};
const datas = this.boradFn(BKMap, ...params);
return datas(type, this.stockSty[2])
};
/**
* 国内指数
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async ZSBoard(type, ...params) {
// [上证指数,深证指数,指数成分]
const ZSMap = {
ZSSH: 'C.1',
ZSSZ: 'C.5',
ZSALL: 'C.IE.ALL'
};
const datas = this.boradFn(ZSMap, ...params);
return datas(type)
};
/**
* 港股通
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async HSGTBoard(type, ...params) {
// [沪股通,深股通,港股通(沪),港股通(深)]
const HSGTMap = {
SH_HK: 'C.BK07071',
SZ_HK: 'C.BK08041',
HK_SH: 'C.MK0144',
HK_SZ: 'C.MK0146',
};
const datas = this.boradFn(HSGTMap, ...params);
return datas(type)
};
/**
* 港股列表
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async HKBoard(type, ...params) {
// [所有港股,主力港股,创新港股,知名港股,港股蓝筹,港股红筹,港股红筹指数成分股,国企股,国企指数成分股,港股通成分股,HS综合大型,HS综合中型,AH比价,ADR,恒生指数]
// 比价参数 client.HKBoard('AH_COMP', 1, 5, '(AB/AH/HKD)').then(res => console.log(res))
const HKMap = {
HKALL: 'C._HKS',
HKMAIN: 'C.MK0107',
HKGEM: 'C.__28GEM',
HKWELL: 'C.MK0009',
HKBLUE: 'C.MK0104',
HKRED: 'C.MK0102',
HKRED_COMP: 'C.__28HSCIINDEX',
HKSTATE: 'C.__28HSCEI',
HKSTATE_COMP: 'C.__28HSCEIINDEX',
HK_COMP: 'C.MK0144',
HSI_LG_COMP: 'C.MK0141',
HSI_MD_COMP: 'C.MK0142',
AH_COMP: 'C._AHH',
HK_ADR: 'C._ADRA',
HS_ZS: 'R.HKI|HKIN|HS',
HK_WARRANTS: 'C._HKW'
};
const datas = this.boradFn(HKMap, ...params);
return datas(type, type !== 'AH_COMP' || type !== 'HS_ZS' || type !== 'HK_WARRANTS' ? this.stockSty[3] : this.stockSty[4])
};
/**
* 美股
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async USBoard(type, ...params) {
// [全部美股,[知名美股,[美科技股,金融,医药食品,媒体,汽车能源,制造零售]],[中国概念,中国互联网],美股指数]
const USMap = {
USALL: 'C._UNS',
USWELL: 'R.MK0216|MK0217|MK0218|MK0219|MK0220|MK0221',
USTECH: 'C.MK0216',
USFINA: 'C.MK0217',
USMEDI_FOOD: 'C.MK0218',
USMEDIA: 'C.MK0219',
USRAUTO_ENGNIN: 'C.MK0220',
USMADE_RETA: 'C.MK0221',
US_CN: 'R.MK0214|MK0212|MK0213|MK0202',
US_CNET: 'C.MK0202',
USZS: 'C._UI_MAP_USOA'
}
const datas = this.boradFn(USMap, ...params);
return datas(type)
};
/**
* 全球指数
*
* @param {*} type
* @param {*} params
* @returns promise
* @memberof boards
*/
async GLOBoard(type, ...params) {
// [亚洲,美洲,欧洲,澳洲]
const GLOMap = {
ASIA: 'R.0000011,3990012,0003001,3990062,3990052,HSI5,HSCEI5,HSCCI5|_UI_MAP_ASIA',
AMERICA: 'C._UI_MAP_AME',
EURO: 'C._UI_MAP_EUR',
AUSTRALIA: 'C._UI_MAP_AUS'
}
const datas = this.boradFn(GLOMap, ...params);
return datas(type, this.stockSty[5])
};
};
module.exports = stocks;
const client = new stocks();
// client.getStockFullInfo('600803,600601').then(res => console.log(res));
// client.HSBoard('CHSA', 1, 2).then(res => console.log(res));
// client.HSBoard('CHSAB_AH', 1, 2, '(AB/AH/USD)').then(res => console.log(res));
// client.BKBoard('BKDY', 1, 5).then(res => console.log(res));
client.ZSBoard('ZSSH', 1, 15).then(res => console.log(res));
// client.HSGTBoard('HK_SH', 1, 5).then(res => console.log(res));
// client.HKBoard('AH_COMP', 1, 5, '(AB/AH/HKD)').then(res => console.log(res));
// client.HKBoard('HK_WARRANTS', 1, 5).then(res => console.log(res));
// client.ZSHSBoard('ALL', 1, 5).then(res => console.log(res));
// client.USBoard('USZS', 1, 5).then(res => console.log(res));
// client.HSBoard('CHSA', 1, 10000).then(res => console.log(res))
// client.FECHKBoard('HKSTOCKF').then(res => console.log(res));

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

Nodejslopo1983 发表了文章 • 0 个评论 • 1543 次浏览 • 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 个评论 • 2268 次浏览 • 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;