node

node

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。
javascript/jQuery

javascript/jQuery

一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。
MongoDB

MongoDB

MongoDB 是一个基于分布式文件存储的数据库
openstack

openstack

OpenStack是一个由NASA(美国国家航空航天局)和Rackspace合作研发并发起的,以Apache许可证授权的自由软件和开放源代码项目。
VUE

VUE

一套构建用户界面的渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。
bootstrap

bootstrap

Bootstrap is the most popular HTML, CSS, and JS framework for developing responsive, mobile first projects on the web.
HTML

HTML

超文本标记语言,标准通用标记语言下的一个应用。
CSS/SASS/SCSS/Less

CSS/SASS/SCSS/Less

层叠样式表(英文全称:Cascading Style Sheets)是一种用来表现HTML(标准通用标记语言的一个应用)或XML(标准通用标记语言的一个子集)等文件样式的计算机语言。
PHP

PHP

PHP(外文名:PHP: Hypertext Preprocessor,中文名:“超文本预处理器”)是一种通用开源脚本语言。语法吸收了C语言、Java和Perl的特点,利于学习,使用广泛,主要适用于Web开发领域。PHP 独特的语法混合了C、Java、Perl以及PHP自创的语法。它可以比CGI或者Perl更快速地执行动态网页。用PHP做出的动态页面与其他的编程语言相比,PHP是将程序嵌入到HTML(标准通用标记语言下的一个应用)文档中去执行,执行效率比完全生成HTML标记的CGI要高许多;PHP还可以执
每天进步一点点

每天进步一点点

乌法把门的各累笑寂静
求职招聘

求职招聘

猎头招聘专用栏目
Python

Python

一种解释型、面向对象、动态数据类型的高级程序设计语言。

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

lopo1983 发表了文章 • 0 个评论 • 1000 次浏览 • 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 公有云计费系统(三)OpenStack 对接 blcokStorage(1)

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

磁盘备份
控制器
'use strict';

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

class OPBlcokStorageVolumeBackupsController extends Controller {
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.QPFN = ctx.helper.queryParamFn;
this.DUFN = ctx.helper.deleteUndefined;
this.snapshotMODEL = ctx.model.Openstack.BlcokStorage.Snapshot;
this.backupSERVICE = ctx.service.openstack.blcokStorage.backup;
}
/**
* @name 磁盘备份列表
*
* @param {String} ctx.query 不需要参数
*
* @description query参数detail存在则显示实力配置列表详细信息,若不传仅显示概要列表
*
* @example GET /openstack/blcokstorage/volume
*
*/
async index() {
const { ctx } = this;
// const datas = !!ctx.isAdmin() ? await this.snapshotMODEL.getAll(this.QPFN(ctx.query)) : await this.snapshotMODEL.getUsers(this.auth.id, this.QPFN(ctx.query));
const datas = await this.backupSERVICE.list(this.QPFN(ctx.query), this.auth.id, ctx.isAdmin())
ctx.body = datas;
}
/**
* @name 获取单一磁盘备份
*
* @description
*
* @example GET /openstack/blcokstorage/snapshot/{id}
*
*/
async show() {
const ctx = this.ctx;
const datas = await this.backupSERVICE.show(ctx.params.id, this.auth.id);
ctx.body = datas;
}
/**
* @name 创建磁盘备份
*
* @param {String} name Optional 磁盘备份名称
* @param {String} description Optional 备注
* @param {String} volume_id 磁盘ID
* @param {Boolean} force Optional 是否备份
* @param {Object} metadata Optional One or more metadata key and value pairs for the snapshot.
*
* @description
*
* @example POST /openstack/blcokstorage/snapshot
*
*/
async create() {
const ctx = this.ctx;
const { name, description, volume_id, force, metadata } = ctx.request.body;
const BODYS = this.DUFN({ name, description, volume_id, force, metadata });
const datas = await this.snapshotMODEL.createOne(this.auth.id, BODYS);
ctx.body = datas
}
/**
* @name 删除备份
*
* @param {*} snapshot_id 备份ID
*
*/
async destroy() {
const ctx = this.ctx;
const datas = await this.backupSERVICE.destroy(this.auth.id, this.auth.ProjectID, ctx.params.id);
ctx.body = datas
}
}

module.exports = OPBlcokStorageVolumeBackupsController;
service
'use strict';
const ServerIndex = require('../index')
//
class VolumeSnapshotService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.adminprojectID = this.config.openstack.projectID.default
this.actions = ':8776/v3'
};
// 获取磁盘备份
async list(query, _ucid, isAdmin) {
const detail = Object.keys(query).includes('detail');
delete query.detail
try {
const datas = await this.OSAJax(`${this.actions}/${!!isAdmin ? this.adminprojectID : await this.getProject(_ucid)}/backups${detail ? '/detail' : ''}`, {
...!isAdmin && { _ucid },
body: { ...isAdmin && { 'all_tenants': true } },
full: true
});
return {
data: {
result: datas,
// totalCount: datas.snapshots.length
}
};
} catch (error) {
return error
}
};
// 获取磁盘备份详情
async show(id, _ucid) {
try {
const datas = await this.OSAJax(`${this.actions}/${await this.getProject(_ucid)}/backups/${id}`, { _ucid });
return { data: datas.snapshot };
} catch (error) {
return error
}
}
// 创建磁盘备份
async create(_ucid, bodys, type = 'manual') {
try {
const datas = await this.OSAJax(`${this.actions}/${await this.getProject(_ucid)}/backups`, {
body: {
'backups': {
... {
"name": `${!bodys.name && `backups-${type}-${Math.random().toString(36).slice(2, 8)}`}`,
"force": true,
"metadata": null
},
...bodys
}
},
method: 'POST',
_ucid
});
return datas
} catch (error) {
return error
}
}
/**
* @name 删除
* @param {*} _ucid 用户id
* @param {*} project_id 密钥对名称
* @param {*} snapshot_id 备份ID
*/
async destroy(_ucid, project_id, snapshot_id) {
try {
const datas = await this.OSAJax(`${this.actions}/${project_id}/backups/${snapshot_id}`, {
_ucid,
method: 'DELETE',
full: true
});
return {
message: datas.status === 404 && `${keypair_name}不存在或已删除`
}
} catch (error) {

}
}
}
module.exports = VolumeSnapshotService;磁盘辅助操作配置
控制器
'use strict';

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

class OPBlcokStorageController extends Controller {
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.DUFN = ctx.helper.deleteUndefined;
this.SERVICE = ctx.service.openstack.blcokStorage.index
};
/**
* name 储存相关配额
*
* @description
*
* @example GET /openstack/blcokstorage/limits
*
* @return {Object}
* - BackupGigabytes 备份可用磁盘可用容量 GB
* - Backups 备份可用个数 个
* - Gigabytes 磁盘可用量 GB
* - Snapshots 磁盘快照可用个数 个
* - Volumes 磁盘可用个数 个
*/
async limits() {
const { ctx } = this;
const DATAS = await this.SERVICE.limits(this.auth.ProjectID, this.auth.id);
ctx.body = DATAS
};
/**
* @name 加载磁盘到万博manbetx手机在线登录网址
*
* @description
*
* @param {String} instance_uuid 万博manbetx手机在线登录网址ID
* @param {String} :id 磁盘ID
*
*/
async attach_volume() {
const ctx = this.ctx;
const { instance_uuid, mode } = ctx.request.body;
const BODYS = this.DUFN({ instance_uuid, mode });
const DATAS = await this.SERVICE.attachVolume(this.auth.id, ctx.params.id, BODYS, this.auth.ProjectID);
ctx.body = DATAS;
};
/**
* @name 卸载磁盘
*
* @description
*
* @param {String} attachment_id 万博manbetx手机在线登录网址ID
* @param {String} :id 磁盘ID
*
*/
async detach_volume() {
const ctx = this.ctx;
const { attachment_id } = ctx.request.body;
const DATAS = await this.SERVICE.detachVolume(this.auth.id, this.auth.ProjectID, ctx.params.id, attachment_id);
ctx.body = DATAS;
};
/**
* @name 创建磁盘镜像
*
* @description
*
* @param {String} image_name 镜像名称
* @param {String} volume_id 磁盘ID
*
*/
async create_image() {
const ctx = this.ctx;
const { image_name, volume_id } = ctx.request.body;
const DATAS = await this.SERVICE.createImage(this.auth.id, this.auth.ProjectID, image_name, volume_id);
ctx.body = DATAS
};
/**
* @name 磁盘扩容
*
* @param {Number} new_size 扩容容量
*/
async new_size_volume() {
const ctx = this.ctx;
const { volumeID } = ctx.params;
const { new_size } = ctx.request.body;
const DATAS = await this.SERVICE.newSizeVolume(this.auth.id, this.auth.ProjectID, volumeID, new_size);
ctx.body = DATAS
};
}

module.exports = OPBlcokStorageController;service
'use strict';
const ServerIndex = require('../index')
//
class volumeService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.nextChars = ctx.helper.nextChars;
this.actions = ':8776/v3'
};
// 获取用户磁盘配额
async limits(project_id, _ucid) {
const DATAS = this.OSAJax(`${this.actions}/${project_id}/limits`, { _ucid })
try {
const { maxTotalBackupGigabytes, maxTotalBackups, maxTotalSnapshots, maxTotalVolumeGigabytes, maxTotalVolumes, totalBackupGigabytesUsed, totalBackupsUsed, totalGigabytesUsed, totalSnapshotsUsed, totalVolumesUsed } = (await DATAS).limits.absolute;
return {
data: {
maxTotalBackupGigabytes,
totalBackupGigabytesUsed,
'BackupGigabytes': maxTotalBackupGigabytes - totalBackupGigabytesUsed,
maxTotalBackups,
totalBackupsUsed,
'Backups': maxTotalBackups - totalBackupsUsed,
maxTotalVolumeGigabytes,
totalGigabytesUsed,
'Gigabytes': maxTotalVolumeGigabytes - totalGigabytesUsed,
maxTotalSnapshots,
totalSnapshotsUsed,
'Snapshots': maxTotalSnapshots - totalSnapshotsUsed,
maxTotalVolumes,
totalVolumesUsed,
'Volumes': maxTotalVolumes - totalVolumesUsed
}
}
} catch (error) {
console.log(error)
return error
}
};
// 磁盘挂载到万博manbetx手机在线登录网址
async attachVolume(_ucid, volume_uuid, body, project_id) {
// const charCount = (await this.OSAJax(`:8774/v2.1/servers/${body.instance_uuid}`, { _ucid })).server['os-extended-volumes:volumes_attached'].length;
// console.log(charCount)

const DATAS = this.OSAJax(`${this.actions}/${!!project_id ? project_id : await this.getProject(_ucid)}/attachments/`, {
body: {
'attachment': {
...body,
volume_uuid
}
},
full: true,
method: 'POST',
_ucid
});
try {
const datas = await DATAS;
return {
code: datas.status,
data: datas.data
}
} catch (error) {}
};
// 磁盘从万博manbetx手机在线登录网址卸载
async detachVolume(_ucid, ProjectID, volume_id, attachment_id) {
const DATAS = this.OSAJax(`${this.actions}/${ProjectID}/volumes/${volume_id}/action`, {
body: {
"os-detach": {
attachment_id
}
},
full: true,
method: 'POST',
_ucid
});
try {
const datas = await DATAS;
return {
code: datas.status,
}
} catch (error) {

}
};
// 扩容
async newSizeVolume(_ucid, project_id, volume_id, new_size) {
try {
const datas = await this.OSAJax(`${this.actions}/${project_id}/volumes/${volume_id}/action`, {
_ucid,
method: 'POST',
body: {
"os-extend": {
new_size
}
},
full: true
});
return {
code: datas.status,
message: datas.status === 400 && `${datas.data.badRequest.message}`
}
} catch (error) {
return error
}
};
// 创建磁盘镜像
async createImage(_ucid, project_id, image_name, volume_id) {
const datas = this.OSAJax(`${this.actions}/${project_id}/volumes/${volume_id}/action`, {
_ucid,
method: 'POST',
body: {
'os-volume_upload_image': {
... {
// "force": false,
// "disk_format": "raw",
// "container_format": "bare",
"visibility": "private",
// "protected": false
},
image_name
}
}
});
try {
throw new Error('无权限')
return {
data: (await datas)['os-volume_upload_image']
}
} catch (error) {
return error
}
};
}
module.exports = volumeService;磁盘快照
控制器
'use strict';

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

class OPBlcokStorageVolumeSnapshotsController extends Controller {
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.QPFN = ctx.helper.queryParamFn;
this.DUFN = ctx.helper.deleteUndefined;
this.snapshotMODEL = ctx.model.Openstack.BlcokStorage.Snapshot;
this.snapshotSERVICE = ctx.service.openstack.blcokStorage.snapshots;
}
/**
* @name 磁盘快照列表
*
* @param {String} ctx.query 不需要参数
*
* @description query参数detail存在则显示实力配置列表详细信息,若不传仅显示概要列表
*
* @example GET /openstack/blcokstorage/volume
*
*/
async index() {
const { ctx } = this;
// const datas = !!ctx.isAdmin() ? await this.snapshotMODEL.getAll(this.QPFN(ctx.query)) : await this.snapshotMODEL.getUsers(this.auth.id, this.QPFN(ctx.query));
const datas = await this.snapshotSERVICE.list(this.QPFN(ctx.query), this.auth.id, ctx.isAdmin())
ctx.body = datas;
}
/**
* @name 获取单一磁盘快照
*
* @description
*
* @example GET /openstack/blcokstorage/snapshot/{id}
*
*/
async show() {
const ctx = this.ctx;
const datas = await this.snapshotSERVICE.show(ctx.params.id, this.auth.id);
ctx.body = datas;
}
/**
* @name 创建磁盘快照
*
* @param {String} name Optional 磁盘快照名称
* @param {String} description Optional 备注
* @param {String} volume_id 磁盘ID
* @param {Boolean} force Optional 是否备份
* @param {Object} metadata Optional One or more metadata key and value pairs for the snapshot.
*
* @description
*
* @example POST /openstack/blcokstorage/snapshot
*
*/
async create() {
const ctx = this.ctx;
const { name, description, volume_id, force, metadata } = ctx.request.body;
const BODYS = this.DUFN({ name, description, volume_id, force, metadata });
const datas = await this.snapshotMODEL.createOne(this.auth.id, BODYS);
ctx.body = datas
}
/**
* @name 删除快照
*
* @param {*} snapshot_id 快照ID
*
*/
async destroy() {
const ctx = this.ctx;
const datas = await this.snapshotSERVICE.destroy(this.auth.id, this.auth.ProjectID, ctx.params.id);
ctx.body = datas
}
}

module.exports = OPBlcokStorageVolumeSnapshotsController;
service
'use strict';
const ServerIndex = require('../index')
//
class VolumeSnapshotService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.adminprojectID = this.config.openstack.projectID.default
this.actions = ':8776/v3'
};
// 获取磁盘快照
async list(query, _ucid, isAdmin) {
const detail = Object.keys(query).includes('detail');
delete query.detail
try {
const datas = await this.OSAJax(`${this.actions}/${!!isAdmin ? this.adminprojectID : await this.getProject(_ucid)}/snapshots${detail ? '/detail' : ''}`, {
...!isAdmin && { _ucid },
body: { ...isAdmin && { 'all_tenants': true } }
});
return {
data: {
result: datas.snapshots,
totalCount: datas.snapshots.length
}
};
} catch (error) {
return error
}
};
// 获取磁盘快照详情
async show(id, _ucid) {
try {
const datas = await this.OSAJax(`${this.actions}/${await this.getProject(_ucid)}/snapshots/${id}`, { _ucid });
return { data: datas.snapshot };
} catch (error) {
return error
}
}
// 创建磁盘快照
async create(_ucid, bodys, type = 'manual') {
try {
const datas = await this.OSAJax(`${this.actions}/${await this.getProject(_ucid)}/snapshots`, {
body: {
'snapshot': {
... {
"name": `${!bodys.name && `Snapshot-${type}-${Math.random().toString(36).slice(2, 8)}`}`,
"force": true,
"metadata": null
},
...bodys
}
},
method: 'POST',
_ucid
});
return datas
} catch (error) {
return error
}
}
/**
* @name 删除
* @param {*} _ucid 用户id
* @param {*} project_id 密钥对名称
* @param {*} snapshot_id 快照ID
*/
async destroy(_ucid, project_id, snapshot_id) {
try {
const datas = await this.OSAJax(`${this.actions}/${project_id}/snapshots/${snapshot_id}`, {
_ucid,
method: 'DELETE',
full: true
});
return {
message: datas.status === 404 && `${keypair_name}不存在或已删除`
}
} catch (error) {

}
}
}
module.exports = VolumeSnapshotService;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 OSblockStorageSnapshotSchema = new Schema({
// 快照类型
type: String,
// 快照状态
// status: String,
// 快照大小
size: Number,
// 快照元信息
metadata: {},
// 快照名称
name: String,
// 快照所属磁盘ID
volume_id: String,
// 创建时间
created_at: Date,
// 描述 prjectID Date
description: String,
// 快照ID
id: String,
// 更新时间
updated_at: Date,
// 公司id
_comp: {
type: Schema.Types.ObjectId,
ref: 'Company',
required: true,
index: true
},
// 状态 ['删除','正常','锁定'] 0,1,2
_status: {
type: Number,
default: 1,
get: v => NumToStr(['删除', '正常', '锁定'], v, true),
},
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
OSblockStorageSnapshotSchema.statics = {
getUsers: async function (_id, queryMix) {
const { querys, select, pages, sort, dates } = queryMix;
const MODELS = this.find({
'_comp': _id,
...querys,
}, {
...select,
})
.limit(!!pages ? pages.limit : 10)
.sort({
'_id': -1,
...sort
});
return {
data: {
result: await MODELS,
totalCount: await MODELS.count()
}
}
},
createOne: async function (_id, body, type) {
const DATAS = await service.openstack.blcokStorage.snapshots.create(_id, body, type);
const MODEL = this.create({
_comp: _id,
...DATAS['snapshot']
});
try {
return { data: await MODEL };
} 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() } };
}
}
return mongoose.model('openstack_block_storage_snapshot', OSblockStorageSnapshotSchema)
}

磁盘类型
控制器
'use strict';

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

class blcokStorageTypesController extends Controller {
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.QMFN = ctx.helper.queryParamFn;
this.deleteUndefined = ctx.helper.deleteUndefined;
this.volumeTypesMODEL = ctx.model.Openstack.BlcokStorage.Types;
this.volumeTypesSERVICE = ctx.service.openstack.blcokStorage.types
}
/**
* @name servers type 列表
*
* @description query参数detail存在则显示实力配置列表详细信息,若不传仅显示概要列表
*
* @example GET /openstack/blcokstorage/
*
*/
async index() {
const {
ctx,
service
} = this;
const datas = await this.volumeTypesMODEL.getAll(this.auth.id, ctx.isAdmin());
// const datas = await this.volumeTypesSERVICE.list(this.auth.id, this.auth.ProjectID, ctx.isAdmin())
ctx.body = datas;
}
}

module.exports = blcokStorageTypesController;service
'use strict';
const ServerIndex = require('../index')
//
class volumeTypesService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.actions = ':8776/v3'
};
// 获取磁盘类型列表
async list(_ucid, project_id, isAdmin) {
const {
config
} = this;
const datas = await this.OSAJax(`${this.actions}/${project_id || config.openstack.projectID.default}/types`, {
...!isAdmin && _ucid
});
try {
return {
data: {
result: datas.volume_types,
totalCount: datas.volume_types.length
}
};
} catch (error) {
return error
}
}
}
module.exports = volumeTypesService;MODEL
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const ctx = app.createAnonymousContext();
const { NumToStr } = ctx.helper;
const OSStorageTypeSchema = new Schema({
// opID
id: String,
// 是否公用
is_public: Boolean,
// 磁盘名称
name: String,
// 磁盘描述备注
description: String,
// 状态 ['删除','正常','锁定'] 0,1,2
status: {
type: Number,
default: 1,
get: v => NumToStr(['删除', '正常', '锁定'], v, true),
},
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
OSStorageTypeSchema.statics = {
// 同步openstack 实例配置
syncFN: async function () {
try {
const datas = (await ctx.service.openstack.blcokStorage.types.list()).data;
if (datas.totalCount) {
let updateModel = datas.result.map(e => this.findOneAndUpdate({ 'id': e.id }, { ...e }, { 'upsert': true, 'new': true }));
let result = await Promise.all(updateModel);
return result;
}
} catch (error) {
return error
}
},
// 获取全部
getAll: async function (queryMix, isAdmin) {
const { querys, select, pages, sort, dates } = queryMix;
const models = this.find({
'status': { '$ne': 0 },
...querys,
// ...(!!pages && !!pages.marker && { '_id': { "$lt": pages.marker } }),
}, {
...select,
...!isAdmin && {
'__v': 0,
'created': 0,
'updated': 0,
'_id': 0
}
})
// .limit(!!pages ? pages.limit : 10)
.sort({
// '_id': -1,
'id': 1,
...sort
});
return { data: { result: await models, totalCount: await models.count() } };
}
}
return mongoose.model('openstack_block_storage_types', OSStorageTypeSchema)
} 查看全部
磁盘备份
控制器
'use strict';

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

class OPBlcokStorageVolumeBackupsController extends Controller {
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.QPFN = ctx.helper.queryParamFn;
this.DUFN = ctx.helper.deleteUndefined;
this.snapshotMODEL = ctx.model.Openstack.BlcokStorage.Snapshot;
this.backupSERVICE = ctx.service.openstack.blcokStorage.backup;
}
/**
* @name 磁盘备份列表
*
* @param {String} ctx.query 不需要参数
*
* @description query参数detail存在则显示实力配置列表详细信息,若不传仅显示概要列表
*
* @example GET /openstack/blcokstorage/volume
*
*/
async index() {
const { ctx } = this;
// const datas = !!ctx.isAdmin() ? await this.snapshotMODEL.getAll(this.QPFN(ctx.query)) : await this.snapshotMODEL.getUsers(this.auth.id, this.QPFN(ctx.query));
const datas = await this.backupSERVICE.list(this.QPFN(ctx.query), this.auth.id, ctx.isAdmin())
ctx.body = datas;
}
/**
* @name 获取单一磁盘备份
*
* @description
*
* @example GET /openstack/blcokstorage/snapshot/{id}
*
*/
async show() {
const ctx = this.ctx;
const datas = await this.backupSERVICE.show(ctx.params.id, this.auth.id);
ctx.body = datas;
}
/**
* @name 创建磁盘备份
*
* @param {String} name Optional 磁盘备份名称
* @param {String} description Optional 备注
* @param {String} volume_id 磁盘ID
* @param {Boolean} force Optional 是否备份
* @param {Object} metadata Optional One or more metadata key and value pairs for the snapshot.
*
* @description
*
* @example POST /openstack/blcokstorage/snapshot
*
*/
async create() {
const ctx = this.ctx;
const { name, description, volume_id, force, metadata } = ctx.request.body;
const BODYS = this.DUFN({ name, description, volume_id, force, metadata });
const datas = await this.snapshotMODEL.createOne(this.auth.id, BODYS);
ctx.body = datas
}
/**
* @name 删除备份
*
* @param {*} snapshot_id 备份ID
*
*/
async destroy() {
const ctx = this.ctx;
const datas = await this.backupSERVICE.destroy(this.auth.id, this.auth.ProjectID, ctx.params.id);
ctx.body = datas
}
}

module.exports = OPBlcokStorageVolumeBackupsController;
service
'use strict';
const ServerIndex = require('../index')
//
class VolumeSnapshotService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.adminprojectID = this.config.openstack.projectID.default
this.actions = ':8776/v3'
};
// 获取磁盘备份
async list(query, _ucid, isAdmin) {
const detail = Object.keys(query).includes('detail');
delete query.detail
try {
const datas = await this.OSAJax(`${this.actions}/${!!isAdmin ? this.adminprojectID : await this.getProject(_ucid)}/backups${detail ? '/detail' : ''}`, {
...!isAdmin && { _ucid },
body: { ...isAdmin && { 'all_tenants': true } },
full: true
});
return {
data: {
result: datas,
// totalCount: datas.snapshots.length
}
};
} catch (error) {
return error
}
};
// 获取磁盘备份详情
async show(id, _ucid) {
try {
const datas = await this.OSAJax(`${this.actions}/${await this.getProject(_ucid)}/backups/${id}`, { _ucid });
return { data: datas.snapshot };
} catch (error) {
return error
}
}
// 创建磁盘备份
async create(_ucid, bodys, type = 'manual') {
try {
const datas = await this.OSAJax(`${this.actions}/${await this.getProject(_ucid)}/backups`, {
body: {
'backups': {
... {
"name": `${!bodys.name && `backups-${type}-${Math.random().toString(36).slice(2, 8)}`}`,
"force": true,
"metadata": null
},
...bodys
}
},
method: 'POST',
_ucid
});
return datas
} catch (error) {
return error
}
}
/**
* @name 删除
* @param {*} _ucid 用户id
* @param {*} project_id 密钥对名称
* @param {*} snapshot_id 备份ID
*/
async destroy(_ucid, project_id, snapshot_id) {
try {
const datas = await this.OSAJax(`${this.actions}/${project_id}/backups/${snapshot_id}`, {
_ucid,
method: 'DELETE',
full: true
});
return {
message: datas.status === 404 && `${keypair_name}不存在或已删除`
}
} catch (error) {

}
}
}
module.exports = VolumeSnapshotService;
磁盘辅助操作配置
控制器
'use strict';

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

class OPBlcokStorageController extends Controller {
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.DUFN = ctx.helper.deleteUndefined;
this.SERVICE = ctx.service.openstack.blcokStorage.index
};
/**
* name 储存相关配额
*
* @description
*
* @example GET /openstack/blcokstorage/limits
*
* @return {Object}
* - BackupGigabytes 备份可用磁盘可用容量 GB
* - Backups 备份可用个数 个
* - Gigabytes 磁盘可用量 GB
* - Snapshots 磁盘快照可用个数 个
* - Volumes 磁盘可用个数 个
*/
async limits() {
const { ctx } = this;
const DATAS = await this.SERVICE.limits(this.auth.ProjectID, this.auth.id);
ctx.body = DATAS
};
/**
* @name 加载磁盘到万博manbetx手机在线登录网址
*
* @description
*
* @param {String} instance_uuid 万博manbetx手机在线登录网址ID
* @param {String} :id 磁盘ID
*
*/
async attach_volume() {
const ctx = this.ctx;
const { instance_uuid, mode } = ctx.request.body;
const BODYS = this.DUFN({ instance_uuid, mode });
const DATAS = await this.SERVICE.attachVolume(this.auth.id, ctx.params.id, BODYS, this.auth.ProjectID);
ctx.body = DATAS;
};
/**
* @name 卸载磁盘
*
* @description
*
* @param {String} attachment_id 万博manbetx手机在线登录网址ID
* @param {String} :id 磁盘ID
*
*/
async detach_volume() {
const ctx = this.ctx;
const { attachment_id } = ctx.request.body;
const DATAS = await this.SERVICE.detachVolume(this.auth.id, this.auth.ProjectID, ctx.params.id, attachment_id);
ctx.body = DATAS;
};
/**
* @name 创建磁盘镜像
*
* @description
*
* @param {String} image_name 镜像名称
* @param {String} volume_id 磁盘ID
*
*/
async create_image() {
const ctx = this.ctx;
const { image_name, volume_id } = ctx.request.body;
const DATAS = await this.SERVICE.createImage(this.auth.id, this.auth.ProjectID, image_name, volume_id);
ctx.body = DATAS
};
/**
* @name 磁盘扩容
*
* @param {Number} new_size 扩容容量
*/
async new_size_volume() {
const ctx = this.ctx;
const { volumeID } = ctx.params;
const { new_size } = ctx.request.body;
const DATAS = await this.SERVICE.newSizeVolume(this.auth.id, this.auth.ProjectID, volumeID, new_size);
ctx.body = DATAS
};
}

module.exports = OPBlcokStorageController;
service
'use strict';
const ServerIndex = require('../index')
//
class volumeService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.nextChars = ctx.helper.nextChars;
this.actions = ':8776/v3'
};
// 获取用户磁盘配额
async limits(project_id, _ucid) {
const DATAS = this.OSAJax(`${this.actions}/${project_id}/limits`, { _ucid })
try {
const { maxTotalBackupGigabytes, maxTotalBackups, maxTotalSnapshots, maxTotalVolumeGigabytes, maxTotalVolumes, totalBackupGigabytesUsed, totalBackupsUsed, totalGigabytesUsed, totalSnapshotsUsed, totalVolumesUsed } = (await DATAS).limits.absolute;
return {
data: {
maxTotalBackupGigabytes,
totalBackupGigabytesUsed,
'BackupGigabytes': maxTotalBackupGigabytes - totalBackupGigabytesUsed,
maxTotalBackups,
totalBackupsUsed,
'Backups': maxTotalBackups - totalBackupsUsed,
maxTotalVolumeGigabytes,
totalGigabytesUsed,
'Gigabytes': maxTotalVolumeGigabytes - totalGigabytesUsed,
maxTotalSnapshots,
totalSnapshotsUsed,
'Snapshots': maxTotalSnapshots - totalSnapshotsUsed,
maxTotalVolumes,
totalVolumesUsed,
'Volumes': maxTotalVolumes - totalVolumesUsed
}
}
} catch (error) {
console.log(error)
return error
}
};
// 磁盘挂载到万博manbetx手机在线登录网址
async attachVolume(_ucid, volume_uuid, body, project_id) {
// const charCount = (await this.OSAJax(`:8774/v2.1/servers/${body.instance_uuid}`, { _ucid })).server['os-extended-volumes:volumes_attached'].length;
// console.log(charCount)

const DATAS = this.OSAJax(`${this.actions}/${!!project_id ? project_id : await this.getProject(_ucid)}/attachments/`, {
body: {
'attachment': {
...body,
volume_uuid
}
},
full: true,
method: 'POST',
_ucid
});
try {
const datas = await DATAS;
return {
code: datas.status,
data: datas.data
}
} catch (error) {}
};
// 磁盘从万博manbetx手机在线登录网址卸载
async detachVolume(_ucid, ProjectID, volume_id, attachment_id) {
const DATAS = this.OSAJax(`${this.actions}/${ProjectID}/volumes/${volume_id}/action`, {
body: {
"os-detach": {
attachment_id
}
},
full: true,
method: 'POST',
_ucid
});
try {
const datas = await DATAS;
return {
code: datas.status,
}
} catch (error) {

}
};
// 扩容
async newSizeVolume(_ucid, project_id, volume_id, new_size) {
try {
const datas = await this.OSAJax(`${this.actions}/${project_id}/volumes/${volume_id}/action`, {
_ucid,
method: 'POST',
body: {
"os-extend": {
new_size
}
},
full: true
});
return {
code: datas.status,
message: datas.status === 400 && `${datas.data.badRequest.message}`
}
} catch (error) {
return error
}
};
// 创建磁盘镜像
async createImage(_ucid, project_id, image_name, volume_id) {
const datas = this.OSAJax(`${this.actions}/${project_id}/volumes/${volume_id}/action`, {
_ucid,
method: 'POST',
body: {
'os-volume_upload_image': {
... {
// "force": false,
// "disk_format": "raw",
// "container_format": "bare",
"visibility": "private",
// "protected": false
},
image_name
}
}
});
try {
throw new Error('无权限')
return {
data: (await datas)['os-volume_upload_image']
}
} catch (error) {
return error
}
};
}
module.exports = volumeService;
磁盘快照
控制器
'use strict';

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

class OPBlcokStorageVolumeSnapshotsController extends Controller {
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.QPFN = ctx.helper.queryParamFn;
this.DUFN = ctx.helper.deleteUndefined;
this.snapshotMODEL = ctx.model.Openstack.BlcokStorage.Snapshot;
this.snapshotSERVICE = ctx.service.openstack.blcokStorage.snapshots;
}
/**
* @name 磁盘快照列表
*
* @param {String} ctx.query 不需要参数
*
* @description query参数detail存在则显示实力配置列表详细信息,若不传仅显示概要列表
*
* @example GET /openstack/blcokstorage/volume
*
*/
async index() {
const { ctx } = this;
// const datas = !!ctx.isAdmin() ? await this.snapshotMODEL.getAll(this.QPFN(ctx.query)) : await this.snapshotMODEL.getUsers(this.auth.id, this.QPFN(ctx.query));
const datas = await this.snapshotSERVICE.list(this.QPFN(ctx.query), this.auth.id, ctx.isAdmin())
ctx.body = datas;
}
/**
* @name 获取单一磁盘快照
*
* @description
*
* @example GET /openstack/blcokstorage/snapshot/{id}
*
*/
async show() {
const ctx = this.ctx;
const datas = await this.snapshotSERVICE.show(ctx.params.id, this.auth.id);
ctx.body = datas;
}
/**
* @name 创建磁盘快照
*
* @param {String} name Optional 磁盘快照名称
* @param {String} description Optional 备注
* @param {String} volume_id 磁盘ID
* @param {Boolean} force Optional 是否备份
* @param {Object} metadata Optional One or more metadata key and value pairs for the snapshot.
*
* @description
*
* @example POST /openstack/blcokstorage/snapshot
*
*/
async create() {
const ctx = this.ctx;
const { name, description, volume_id, force, metadata } = ctx.request.body;
const BODYS = this.DUFN({ name, description, volume_id, force, metadata });
const datas = await this.snapshotMODEL.createOne(this.auth.id, BODYS);
ctx.body = datas
}
/**
* @name 删除快照
*
* @param {*} snapshot_id 快照ID
*
*/
async destroy() {
const ctx = this.ctx;
const datas = await this.snapshotSERVICE.destroy(this.auth.id, this.auth.ProjectID, ctx.params.id);
ctx.body = datas
}
}

module.exports = OPBlcokStorageVolumeSnapshotsController;
service
'use strict';
const ServerIndex = require('../index')
//
class VolumeSnapshotService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.adminprojectID = this.config.openstack.projectID.default
this.actions = ':8776/v3'
};
// 获取磁盘快照
async list(query, _ucid, isAdmin) {
const detail = Object.keys(query).includes('detail');
delete query.detail
try {
const datas = await this.OSAJax(`${this.actions}/${!!isAdmin ? this.adminprojectID : await this.getProject(_ucid)}/snapshots${detail ? '/detail' : ''}`, {
...!isAdmin && { _ucid },
body: { ...isAdmin && { 'all_tenants': true } }
});
return {
data: {
result: datas.snapshots,
totalCount: datas.snapshots.length
}
};
} catch (error) {
return error
}
};
// 获取磁盘快照详情
async show(id, _ucid) {
try {
const datas = await this.OSAJax(`${this.actions}/${await this.getProject(_ucid)}/snapshots/${id}`, { _ucid });
return { data: datas.snapshot };
} catch (error) {
return error
}
}
// 创建磁盘快照
async create(_ucid, bodys, type = 'manual') {
try {
const datas = await this.OSAJax(`${this.actions}/${await this.getProject(_ucid)}/snapshots`, {
body: {
'snapshot': {
... {
"name": `${!bodys.name && `Snapshot-${type}-${Math.random().toString(36).slice(2, 8)}`}`,
"force": true,
"metadata": null
},
...bodys
}
},
method: 'POST',
_ucid
});
return datas
} catch (error) {
return error
}
}
/**
* @name 删除
* @param {*} _ucid 用户id
* @param {*} project_id 密钥对名称
* @param {*} snapshot_id 快照ID
*/
async destroy(_ucid, project_id, snapshot_id) {
try {
const datas = await this.OSAJax(`${this.actions}/${project_id}/snapshots/${snapshot_id}`, {
_ucid,
method: 'DELETE',
full: true
});
return {
message: datas.status === 404 && `${keypair_name}不存在或已删除`
}
} catch (error) {

}
}
}
module.exports = VolumeSnapshotService;
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 OSblockStorageSnapshotSchema = new Schema({
// 快照类型
type: String,
// 快照状态
// status: String,
// 快照大小
size: Number,
// 快照元信息
metadata: {},
// 快照名称
name: String,
// 快照所属磁盘ID
volume_id: String,
// 创建时间
created_at: Date,
// 描述 prjectID Date
description: String,
// 快照ID
id: String,
// 更新时间
updated_at: Date,
// 公司id
_comp: {
type: Schema.Types.ObjectId,
ref: 'Company',
required: true,
index: true
},
// 状态 ['删除','正常','锁定'] 0,1,2
_status: {
type: Number,
default: 1,
get: v => NumToStr(['删除', '正常', '锁定'], v, true),
},
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
OSblockStorageSnapshotSchema.statics = {
getUsers: async function (_id, queryMix) {
const { querys, select, pages, sort, dates } = queryMix;
const MODELS = this.find({
'_comp': _id,
...querys,
}, {
...select,
})
.limit(!!pages ? pages.limit : 10)
.sort({
'_id': -1,
...sort
});
return {
data: {
result: await MODELS,
totalCount: await MODELS.count()
}
}
},
createOne: async function (_id, body, type) {
const DATAS = await service.openstack.blcokStorage.snapshots.create(_id, body, type);
const MODEL = this.create({
_comp: _id,
...DATAS['snapshot']
});
try {
return { data: await MODEL };
} 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() } };
}
}
return mongoose.model('openstack_block_storage_snapshot', OSblockStorageSnapshotSchema)
}

磁盘类型
控制器
'use strict';

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

class blcokStorageTypesController extends Controller {
constructor(ctx) {
super(ctx);
this.auth = ctx.state.user;
this.QMFN = ctx.helper.queryParamFn;
this.deleteUndefined = ctx.helper.deleteUndefined;
this.volumeTypesMODEL = ctx.model.Openstack.BlcokStorage.Types;
this.volumeTypesSERVICE = ctx.service.openstack.blcokStorage.types
}
/**
* @name servers type 列表
*
* @description query参数detail存在则显示实力配置列表详细信息,若不传仅显示概要列表
*
* @example GET /openstack/blcokstorage/
*
*/
async index() {
const {
ctx,
service
} = this;
const datas = await this.volumeTypesMODEL.getAll(this.auth.id, ctx.isAdmin());
// const datas = await this.volumeTypesSERVICE.list(this.auth.id, this.auth.ProjectID, ctx.isAdmin())
ctx.body = datas;
}
}

module.exports = blcokStorageTypesController;
service
'use strict';
const ServerIndex = require('../index')
//
class volumeTypesService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.actions = ':8776/v3'
};
// 获取磁盘类型列表
async list(_ucid, project_id, isAdmin) {
const {
config
} = this;
const datas = await this.OSAJax(`${this.actions}/${project_id || config.openstack.projectID.default}/types`, {
...!isAdmin && _ucid
});
try {
return {
data: {
result: datas.volume_types,
totalCount: datas.volume_types.length
}
};
} catch (error) {
return error
}
}
}
module.exports = volumeTypesService;
MODEL
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const ctx = app.createAnonymousContext();
const { NumToStr } = ctx.helper;
const OSStorageTypeSchema = new Schema({
// opID
id: String,
// 是否公用
is_public: Boolean,
// 磁盘名称
name: String,
// 磁盘描述备注
description: String,
// 状态 ['删除','正常','锁定'] 0,1,2
status: {
type: Number,
default: 1,
get: v => NumToStr(['删除', '正常', '锁定'], v, true),
},
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
OSStorageTypeSchema.statics = {
// 同步openstack 实例配置
syncFN: async function () {
try {
const datas = (await ctx.service.openstack.blcokStorage.types.list()).data;
if (datas.totalCount) {
let updateModel = datas.result.map(e => this.findOneAndUpdate({ 'id': e.id }, { ...e }, { 'upsert': true, 'new': true }));
let result = await Promise.all(updateModel);
return result;
}
} catch (error) {
return error
}
},
// 获取全部
getAll: async function (queryMix, isAdmin) {
const { querys, select, pages, sort, dates } = queryMix;
const models = this.find({
'status': { '$ne': 0 },
...querys,
// ...(!!pages && !!pages.marker && { '_id': { "$lt": pages.marker } }),
}, {
...select,
...!isAdmin && {
'__v': 0,
'created': 0,
'updated': 0,
'_id': 0
}
})
// .limit(!!pages ? pages.limit : 10)
.sort({
// '_id': -1,
'id': 1,
...sort
});
return { data: { result: await models, totalCount: await models.count() } };
}
}
return mongoose.model('openstack_block_storage_types', OSStorageTypeSchema)
}

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

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

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

lopo1983 发表了文章 • 0 个评论 • 1164 次浏览 • 2019-03-14 00:13 • 来自相关话题

鉴于Openstack的特殊性 和项目需求,用户系统将分为 系统用户 管理员用户 openstack用户 大致3类
一 系统用户
表设计const crypto = require('crypto');
const Decimal = require('decimal');

module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const ctx = app.createAnonymousContext();
const {
NumToStr
} = ctx.helper;
const CompanySchema = new Schema({
// 公司名称
compName: {
type: String,
index: true
},
// 认证类型 [0/未认证,1/个人,2/企业]
compAuthType: {
type: Number,
default: 0,
get: v => NumToStr(['未认证', '个人', '企业'], v, true),
},
// 认证状态 [0/'未认证', 1/'已上传', 2/'认证中',3/ '认证未通过', 4/'已认证']
compAuthStatus: {
type: Number,
default: 0,
get: v => NumToStr(['未认证', '已上传', '认证中', '认证未通过', '已认证'], v, true),
},
// 认证证件信息相关
compLicense: {
type: Schema.Types.ObjectId,
ref: 'CompLicense',
},
// 关联绑定
compAuth: {
// 邮件绑定
email: {
type: Boolean,
default: false
},
// 手机绑定
phone: {
type: Boolean,
default: true
}
},
// 企业邮箱
compEmail: {
type: String,
required: [true, '企业邮箱不允许为空'],
unique: true,
},
compName: {
type: String,
},
// 企业联系电话
compPhone: {
type: String,
required: [true, '联系电话不允许为空'],
unique: true,
},
// 登陆密码
compPassword: {
type: String,
required: [true, '密码不能为空'],
minlength: [8, '密码应大于8位数'],
set: v => {
let pilipala = Math.random().toString(36).slice(2, 8);
let bilibole = crypto.createHash('md5').update(`${v}:${pilipala}`).digest('hex');
return `${pilipala}${bilibole}`;
}
},
// 企业服务
compService: ,
// 企业产品
compProduct: ,
// 企业api 需求
compApi: [{
name: {
type: String,
},
IPWhiteList: [String]
}],
// 用户ak sk
compASK: ,
// 用户账户余额
compAccount: {
// 现金账户
cash: {
type: Number,
default: 0.00,
},
// 代理账户
agent: {
type: Number,
default: 0.00
},
// 代金券账户
other: {
type: Number,
default: 0.00
}
},
// 余额预警
compAccountWarnig: {
type: Number,
default: 0
},
// 客户编号
compNumer: {
type: String
},
// 公司客服经理
compMaster: {
type: String
},
// 用户代理等级 [1-5]
compAgent: {
type: Number,
default: 1,
get: v => NumToStr(['普通', '铜牌', '银牌', '金牌', '白金', '钻石'], v),
},
// 公司客户
compCustomer: [{
type: Schema.Types.ObjectId,
ref: 'Company',
}],
// 用户优惠券
compCoupon: {
type: Array
},
// 购物车ID
shoppingCart: {
type: Schema.Types.ObjectId,
ref: 'ShopCar',
},
// 签名系统状态 ['删除','正常','锁定'] 0,1,2
status: {
type: Number,
default: 1,
// get: v => NumToStr(['删除', '正常', '锁定'], v, true),
},
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
const isAdmin = (auth) => auth.group === 1 ? 0 : 1;
CompanySchema.statics = {
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,
'compPassword': 0,
'compASK': 0
})
.limit(!!pages ? pages.limit : 10)
.sort({
'_id': -1,
...sort
});
return {
data: {
result: await models,
totalCount: await models.count()
}
};
},
getOne: async function(_id) {
const models = this.findOne({
_id,
'status': {
'$ne': 0
}
}, {
'compPassword': 0,
'compASK': 0
});
return {
data: await models
}
},
/**
* 账户余额操作
* @param {Number} money 金额
* @param {Object} auth admin鉴权
* @param {String} type 余额类型
* @param {String} method 类型 add + sub-
* @param {ObjectId} _id 公司id
*/
cashFN: async function(money, auth, type = 'cash', method = 'add', _id) {
try {
const {
compAccount,
compName
} = await this.findOne({
'_id': _id ? _id : auth.id
});
const nowCash = new Decimal(compAccount[type])[method](new Decimal(money)).toNumber();
let result;
if (type = 'cash') {
result = await this.update({
'_id': _id ? _id : auth.id
}, {
'compAccount.cash': nowCash
});
} else if (type = 'agent') {
result = await this.update({
'_id': _id ? _id : auth.id
}, {
'compAccount.agent': nowCash
});
} else {
result = await this.update({
'_id': _id ? _id : auth.id
}, {
'compAccount.other': nowCash
});
};
if (auth.group === 1) await this.cashWarning(ctx, _id);
return result.ok === 1 ? `${compName}${method === 'add' ? `充值¥:${money}元成功!` : '购买成功'}` : `${compName}充值失败`;
} catch (error) {
return error
}
},
// 余额预警
cashWarning: async function (ctx, _id) {
const {
compAccount,
compAccountWarnig,
compName
} = await this.findOne({
_id
});
const cashCount = Object.values(compAccount).reduce((a, b) => a + b);
if (compAccountWarnig >= cashCount) return await ctx.helper.sendWarning(ctx, `您的现金账户余额不足¥:${compAccountWarnig},为了保证您业务的正常运行,请及时充值!`, _id, compName);
},
// 登陆获取token
isLogin: async function (ctx) {
return await ctx.service.mongo.adcq.isLogin(ctx, 'Company', 'compPassword', async (e) => {
let {
_id,
compPhone,
compEmail,
compName,
compAuth,
compService,
updated,
compApi
} = e;
const ProjectID = (await ctx.model.Openstack.Identity.User.getObjectID(_id)).default_project_id;
try {
const TOKEN = await ctx.service.apps.jwt.sign({
id: _id,
group: 1,
phone: compPhone,
compApi,
ProjectID
});
// await ctx.model.Token.addOne({ authToken: TOKEN, updateTime: new Date() });
return {
token: TOKEN,
info: {
_id,
compPhone,
compEmail,
compName,
compAuth,
compService,
updated,
compApi
}
}
} catch (error) {
return error
}
})
},
// ak/sk生成token
apiToken: async function (body) {
const {
uid,
api_key,
secret_key
} = body;
const userInfo = await this.findOne({
_id: uid
}, {
'compASK': 1,
'compApi': 1,
});
const isHave = userInfo.compASK.filter(e => e.ak == api_key && e.sk == secret_key).length;
if (isHave === 1) {
let {
_id,
compApi
} = userInfo;
return {
data: {
token: await ctx.service.apps.jwt.sign({
id: _id,
group: 3,
service: compApi,
}),
expiresIn: 60 * 60 * 24 * 30 * 1000
}
}
} else {
return {
code: 422,
message: '参数不正确'
}
}
},
// 获取ask 列表
getASK: async function (_id) {
try {
const result = await this.findOne(_id);
return {
data: result.compASK
}
} catch (error) {}
},
// 创建ask
createASK: async function (ctx) {
if ((await this.getASK({
'_id': ctx.state.user.id
})).data.length === 20) return {
message: '每一用户只允许创建20个ak/sk序列对!',
code: 422
};
const asks = {
_id: mongoose.Types.ObjectId(),
info: '',
ak: crypto.createHash('md5').update(`${Math.random().toString(36).slice(2, 5)}`).digest('hex'),
sk: crypto.createHash('md5').update(`${Math.random().toString(36).slice(2, 8)}`).digest('hex'),
createTime: new Date()
};
return {
data: (await this.findOneAndUpdate({
'_id': ctx.state.user.id
}, {
$push: {
'compASK': asks
}
})).compASK.pop()
}
},
// 设置ask 名称
setInfo: async function (ctx) {
const {
info
} = ctx.request.body;
const result = await this.update({
'_id': ctx.params.id,
'compASK._id': mongoose.Types.ObjectId(ctx.request.body.id)
}, {
'$set': {
'compASK.$.info': info
}
});
return result.n === 1 && {
data: {
_id: ctx.params.id,
info: info
}
}
},
// 删除ak
delASK: async function name(ctx) {
const result = await this.update({
'_id': ctx.state.user.id
}, {
"$pull": {
'compASK': {
'_id': mongoose.Types.ObjectId(ctx.params.id)
}
}
});
return result.n === 1 && {
data: ctx.params.id
};
},
// 锁定公司
lockfn: async function (ctx, type) {
return {
data: await this.findOneAndUpdate({
'_id': ctx.params.id
}, {
$set: {
'status': 2
}
})
}
},
// 是否已存在
isIn: async function (params) {
return await this.findOne({ ...params
})
},
// 获取公司详细
getComp: async function (auth) {
return (await this.findOne({ ...(!isAdmin(auth) && {
'_id': auth.id
})
}))
},
// 获取现金
getCash: async function (auth) {
return (await this.getComp(auth)).compAccount
},
// 获取优惠券
getCoupon: async function (auth) {
const coupon = (await this.getComp(auth)).compCoupon;
return {
coupon: coupon,
length: coupon.length
}
},
// 获取服务
getService: async function (auth) {
return (await this.getComp(auth)).compService
},
// 获取客户列表
getCustomerList: async function (auth) {
const List = (await this.findOne({
'_id': auth.id
})).compCustomer;
const result = await this.find({
_id: {
$in: List
}
}, 'compName compEmail compPhone created');
return result;
},
// 绑定邮箱
bindMail: async function (auth) {
const result = await this.update({
'_id': auth.id
}, {
'compAuth.emailAuth': true
}, {
upsert: true
});
return result.ok === 1 && true;
},
// 登陆注册找回密码 pipe
lrfAction: async function (body) {
const {
action,
compPhone: SmsPhone,
code: SmsCode
} = body;
delete body.action
if (action === 'changePsd') {
const isInUsers = await this.isIn({
'compPhone': body.compPhone
});
if (!isInUsers) return {
message: '对不起您还不是我们的客户,请先注册!'
};
}
const checkCode = await ctx.model.SmsCode.check({
SmsPhone,
SmsCode
});
delete body.code
if (checkCode !== true) return checkCode;
return this[action](body);
},
// 注册
regist: async function (body) {
const {
compEmail
} = body;
try {
const result = await this.create(body);
if (result._id) {
await ctx.service.sms.mail.sendMail({
to: compEmail,
html: 'regist',
});
await ctx.model.System.Ui.User.create({
'_comp': result._id
});
const datas = await ctx.model.Openstack.Identity.User.createUser(result._id);
return {
code: 201,
data: {
name: result._id,
}
}
};
} catch (error) {
return error
}
},
// 修改密码
changePsd: async function (body) {
const {
compPhone,
compPassword
} = body;
return await this.findOneAndUpdate({
'compPhone': compPhone
}, {
'compPassword': compPassword
})
},
// 绑定客户
bindCustomer: async function (_id, auth) {
return await this.findOneAndUpdate({
_id
}, {
$push: {
'compCustomer': auth.id
}
})
},
}
return mongoose.model('Company', CompanySchema);
}控制器/*************************************************************
*
*- Copyright (c) qiduo, 2018
*- FileName: USERController.js 用户基本信息相关
*- Author: 罗波 lopo1983@vip.qq.com
*- Version: 1.0
*- Date:2018-10-15
*- Descripttion:openstack geecp
*- Modules:
*
*- Extends
* - 1.egg_Controller
*----------------------------------------------------------
*
*- Function List :
*
*- StaticsList :
* - index 获取用户列表
* - isHave 检测用户是否存在
* - show 获取单一用户信息
* - create 用户注册
*
*- History :
* <Author> <Date> <Desc>
*
**************************************************************/
'use strict';

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

class USERController 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 获取用户列表
*
* @description Admin 用户可用 可使用queryParamFn 进行查询
*
* @example GET /user/
*
*/
async index() {
const ctx = this.ctx;
const { helper } = ctx;
const result = await this.MODEL.getAll(helper.queryParamFn(ctx.query));
ctx.body = result
}
/**
*
* @name 检测用户是否存在
*
* @param {String} compName 用户名称
* @param {String} compEmail 用户邮箱
* @param {String} compPhone 用户手机
*
* @description compName, compEmail, compPhone 三选一 或可多选
*
* @example GET /user/isIn/?@param = param
*
*/
async isHave() {
const ctx = this.ctx;
const { compName, compEmail, compPhone } = ctx.query;
const result = (await this.MODEL.isIn(this.DUFn({ compName, compEmail, compPhone }))) === null;
ctx.body = { data: `${!result}` }
}
/**
*
* @name 获取用户信息
* @param {ObjectId} id 必传 ctx.params.id 用户id
*
* @example GET /user/{id}
*/
async show() {
const ctx = this.ctx;
const result = await this.MODEL.getOne({ '_id': ctx.params.id });
ctx.body = result
}
/**
*
* @name 用户注册 body = ctx.request.body
* @param {String} action 必传 动作 regist changePsd
* @param {String} compEmail 必传 邮箱
* @param {String} compPhone 必传 手机号码
* @param {String} compPassword 必传 用户密码 `${SALT}${MD5}`
* @param {String} code 必传 手机验证码,120s次 mongodb.smscodes expiresIn_TTL_60*3
*
* @example POST /user/
*
* @description
* - 邮箱和手机均能登陆
* - compEmail, compPhone, compPassword, action, code 均为必选参数
*
*/
async create() {
const { ctx } = this;
const { compEmail, compPhone, compPassword, action, code } = ctx.request.body;
const result = await this.MODEL.lrfAction({ compEmail, compPhone, compPassword, action, code });
ctx.body = result
}
/**
* @name 修改用户信息
*
* @param {String} action 必传 修改的方法
* @param {action}
* - changePsd 修改密码
* - bindCustomer 绑定客户
* - lrfAction 找回密码
* -
*
* @example PUT /user/{ObjecId}
*/
async update() {
const { ctx } = this;
const bodys = ctx.request.body;
const { action } = bodys;
delete bodys.action;
const result = await this.MODEL[action](bodys)
}
}

module.exports = USERController;

二 管理员用户
表设计const crypto = require('crypto');
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const AdminSchema = new Schema({
// 用户名
adminName: {
type: String,
required: true,
unique: [true, '用户名已存在']
},
// 用户密码
adminPassword: {
type: String,
required: true,
set: v => {
let pilipala = Math.random().toString(36).slice(2, 8);
let bilibole = crypto.createHash('md5').update(`${v}:${pilipala}`).digest('hex');
return `${pilipala}${bilibole}`;
}
},
// 用户权限组
adminGroup: {
type: Number,
required: true,
default: 2
},
// 用户真实姓名
adminRealName: {
type: String
},
// 用户头像
adminHeader: {
type: String,
default: ''
},
adminStaff: {
// type: Schema.Types.ObjectId,
// ref: 'Staff',
},
status: {
type: Number,
default: 1
},
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
AdminSchema.statics = {
isLogin: async function (ctx) {
return await ctx.service.mongo.adcq.isLogin(ctx, 'Admin', 'adminPassword', async (e) => {
let { _id, adminRealName, adminName, adminGroup, adminHeader, adminStaff } = e;
return {
token: await ctx.service.apps.jwt.sign({ id: _id, group: adminGroup }),
info: { _id, adminRealName, adminName, adminHeader, adminStaff }
}
})
}
}
return mongoose.model('Admin', AdminSchema)
}OpenStack 用户
表设计const crypto = require('crypto');
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const { ctx, helper, service } = app.createAnonymousContext();
const OSIdentityUserSchema = new Schema({
// The user ID.
id: String,
// The ID of the default project for the user.
default_project_id: String,
// user description
description: String,
// The ID of the domain.
domain_id: String,
// user emaill address
email: String,
// If the user is enabled, this value is true. If the user is disabled, this value is false.
enabled: Boolean,
// The links for the user resource.
links: {},
// user name
name: String,
// 密码
password: String,
// 角色ID
role: String,
// 同步状态
sync: Boolean
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
OSIdentityUserSchema.statics = {
createUser: async function(_id) {
const setPSDFn = (v = _id) => {
let pilipala = Math.random().toString(36).slice(2, 8);
let bilibole = crypto.createHash('md5').update(`${v}:${pilipala}`).digest('hex');
return `${pilipala}${bilibole}`;
};
const password = setPSDFn();
try {
/****************************创建Project*********************************/
const PROJECT = await service.openstack.identity.projects.create({
name: _id,
'description': `comp_id:${_id} date:${new Date() -0 }`
});
const default_project_id = PROJECT.data.project.id;
// || PROJECT.data.error.code === 409
if (PROJECT.data.project) {
const DATAS = await service.openstack.identity.users.create({
'name': _id,
password,
default_project_id,
'description': `comp_id:${_id} date:${new Date()-0 }`
});
/******************************本地创建*******************************/
const result = await this.create({
...DATAS.data.user,
password,
default_project_id,
sync: true
});
/******************************分配角色*******************************/
if (!result.id) return;
await service.openstack.identity.roles.update(result.default_project_id, result.id, app.config.openstack.rolesID.default);
return result
}
} catch (error) {
return error
}
},
/**
* @name 获取一个
* @param {*} _id compid
* @param {*} select 选择的字段(BSON name/显示的名称 -name/不显示的名称)
*/
getOne: async function(_id, select) {
const MODEL = this.findOne({ name: _id }, !!select && select);
try {
const result = await MODEL;
return !!result ? result : await this.createUser(_id);
} catch (error) {
return error
}
},
// 获取ObjectID
getObjectID: async function(_id) {
const MODEL = this.findOne({ 'name': _id }, 'default_project_id -_id')
try {
return await MODEL
} catch (error) {
return error
}
}
}
return mongoose.model('openstack_Identity_user', OSIdentityUserSchema)
}service?
role'use strict';
const ServerIndex = require('../index')
//
class identityRolesService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.actions = ':5000/identity/v3/'
};
// 获取Region列表
async list(parent_region_id) {
try {
const datas = await this.OSAJax(`${this.actions}`, { body: { parent_region_id } })
return {
data: {
result: datas.regions,
totalCount: datas.regions.length
}
};
} catch (error) {
return {
data: error
}
}

}
// 显示regions详情
async show(project_id, user_id) {
let datas
try {
datas = await this.OSAJax(`${this.actions}projects/${project_id}/users/${user_id}/roles`)
} catch (error) {
datas = error
}
return datas
}
// 创建regions
async create(bodys) {
}
// 给用户赋予角色
async update(project_id, user_id, role_id) {
let datas;
try {
datas = await this.OSAJax(`${this.actions}projects/${project_id}/users/${user_id}/roles/${role_id}`, { method: 'PUT' });
} catch (error) {
datas = error
}
return { data: datas }
}
// 删除regions
async destroy(id) {
}
}
module.exports = identityRolesService;project'use strict';
const ServerIndex = require('../index')
//
class identityProjectService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.actions = ':5000/identity/v3/projects'
};
// 获取Projects列表
async list() {
const datas = await this.OSAJax(`${this.actions}`);
return {
data: {
'result': datas.projects,
'totalCount': datas.projects.length
}
};
}
// 显示Projects详情
async show(id) {
const datas = await this.OSAJax(`${this.actions}/${id}`);
return { data: datas };
}
// 创建Projects
async create(bodys) {
const datas = await this.OSAJax(this.actions, { body: { 'project': bodys }, method: 'POST' });
return { data: datas }
}
// 修改Projects
async update(id, bodys) {
const datas = await this.OSAJax(`${this.actions}/${id}`, { body: { 'project': bodys }, method: 'PATCH' });
return { data: datas }
}
// 删除Projects
async destroy(id) {
const datas = await this.OSAJax(`${this.actions}/${id}`, { method: 'DELETE' });
return { data: datas }
}
}
module.exports = identityProjectService;控制器'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;共用service
'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) : false);
method !== 'DELETE' && Object.assign(opt.data, {...!!body ? body : ''
});
// console.log(opt)
// console.log({
// // 'getFullRES': full,
// // 'isAdmin': !!_ucid ? `No | _ucid:${_ucid}` : 'Yes',
// 'URI': `http://${this.uri}${!!params ? params : ''}`,
// // 'Method': method,
// // ...method !== 'DELETE' && {
// // 'Body': opt.data
// // },
// // '_': new Date()
// })
try {
const result = await ctx.curl(`${this.uri}${!!params ? params : ''}`, opt);
return !!full && result || !!status && result.status || result.data;
} catch (error) {
return error
}
};
// 获取普通用户token
async getUserToken(_id) {
const ctx = this.ctx;
return (await ctx.model.Openstack.Token.getToken(_id)).data
};
// 获取普通用户projectID
async getProject(_id) {
const ctx = this.ctx;
return _id !== 'admin' ? (await this.OPUserMODEL.getOne(_id, 'default_project_id'))['default_project_id'] : this.config.openstack.projectID.default
};
// 从支付订单创建相关实例
/**
*
* @param {*} _comp 公司id
* @param {*} model 数据库
* @param {*} bodys 内容
* +@param {String} _metadata 关联数据
* @param {*} time 开通时长
*/
async createFromOrder(_comp, model, bodys, time, payment) {
try {
const ctx = this.ctx;
const MODELS = ctx.model.Openstack;
const {
deleteUndefined: DUFn,
subtractMoment: TMFn
} = ctx.helper
let datas = {}
if (model === 'cds') {
const {
availability_zone,
snapshot_id,
backup_id,
imageRef,
size,
volume_type,
_metadata
} = bodys;
const BODYS = DUFn({
availability_zone,
snapshot_id,
backup_id,
imageRef,
size,
volume_type,
_metadata
});
datas = await MODELS.BlcokStorage.Volumes.createOne(
_comp, {
...BODYS,
'endTime': TMFn(time, 'month', 'add'),
payment
}
);
}
if (model === 'ecs') {
const {
availability_zone,
flavorRef,
name,
uuid,
imageRef,
server_password,
key_name,
security_groups,
_metadata
} = bodys;
const SERVERBODYS = DUFn({
availability_zone,
flavorRef,
name,
uuid,
imageRef,
server_password,
key_name,
security_groups,
_metadata
});
datas = await MODELS.Servers.Server.createServer(
_comp, {
...SERVERBODYS,
'endTime': TMFn(time, 'month', 'add'),
payment,
}
);
}
if (model === 'eip') {
const {
description,
_metadata
} = bodys;
const EIPBODYS = DUFn({
description,
_metadata
});
datas = await MODELS.Neutron.FloatingIP.createOne(
_comp, {
...EIPBODYS,
'endTime': TMFn(time, 'month', 'add'),
payment
})
}
return datas
} catch (error) {
console.log(error)
}
}
}
module.exports = indexService; 查看全部

鉴于Openstack的特殊性 和项目需求,用户系统将分为 系统用户 管理员用户 openstack用户 大致3类


一 系统用户
表设计
const crypto = require('crypto');
const Decimal = require('decimal');

module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const ctx = app.createAnonymousContext();
const {
NumToStr
} = ctx.helper;
const CompanySchema = new Schema({
// 公司名称
compName: {
type: String,
index: true
},
// 认证类型 [0/未认证,1/个人,2/企业]
compAuthType: {
type: Number,
default: 0,
get: v => NumToStr(['未认证', '个人', '企业'], v, true),
},
// 认证状态 [0/'未认证', 1/'已上传', 2/'认证中',3/ '认证未通过', 4/'已认证']
compAuthStatus: {
type: Number,
default: 0,
get: v => NumToStr(['未认证', '已上传', '认证中', '认证未通过', '已认证'], v, true),
},
// 认证证件信息相关
compLicense: {
type: Schema.Types.ObjectId,
ref: 'CompLicense',
},
// 关联绑定
compAuth: {
// 邮件绑定
email: {
type: Boolean,
default: false
},
// 手机绑定
phone: {
type: Boolean,
default: true
}
},
// 企业邮箱
compEmail: {
type: String,
required: [true, '企业邮箱不允许为空'],
unique: true,
},
compName: {
type: String,
},
// 企业联系电话
compPhone: {
type: String,
required: [true, '联系电话不允许为空'],
unique: true,
},
// 登陆密码
compPassword: {
type: String,
required: [true, '密码不能为空'],
minlength: [8, '密码应大于8位数'],
set: v => {
let pilipala = Math.random().toString(36).slice(2, 8);
let bilibole = crypto.createHash('md5').update(`${v}:${pilipala}`).digest('hex');
return `${pilipala}${bilibole}`;
}
},
// 企业服务
compService: ,
// 企业产品
compProduct: ,
// 企业api 需求
compApi: [{
name: {
type: String,
},
IPWhiteList: [String]
}],
// 用户ak sk
compASK: ,
// 用户账户余额
compAccount: {
// 现金账户
cash: {
type: Number,
default: 0.00,
},
// 代理账户
agent: {
type: Number,
default: 0.00
},
// 代金券账户
other: {
type: Number,
default: 0.00
}
},
// 余额预警
compAccountWarnig: {
type: Number,
default: 0
},
// 客户编号
compNumer: {
type: String
},
// 公司客服经理
compMaster: {
type: String
},
// 用户代理等级 [1-5]
compAgent: {
type: Number,
default: 1,
get: v => NumToStr(['普通', '铜牌', '银牌', '金牌', '白金', '钻石'], v),
},
// 公司客户
compCustomer: [{
type: Schema.Types.ObjectId,
ref: 'Company',
}],
// 用户优惠券
compCoupon: {
type: Array
},
// 购物车ID
shoppingCart: {
type: Schema.Types.ObjectId,
ref: 'ShopCar',
},
// 签名系统状态 ['删除','正常','锁定'] 0,1,2
status: {
type: Number,
default: 1,
// get: v => NumToStr(['删除', '正常', '锁定'], v, true),
},
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
const isAdmin = (auth) => auth.group === 1 ? 0 : 1;
CompanySchema.statics = {
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,
'compPassword': 0,
'compASK': 0
})
.limit(!!pages ? pages.limit : 10)
.sort({
'_id': -1,
...sort
});
return {
data: {
result: await models,
totalCount: await models.count()
}
};
},
getOne: async function(_id) {
const models = this.findOne({
_id,
'status': {
'$ne': 0
}
}, {
'compPassword': 0,
'compASK': 0
});
return {
data: await models
}
},
/**
* 账户余额操作
* @param {Number} money 金额
* @param {Object} auth admin鉴权
* @param {String} type 余额类型
* @param {String} method 类型 add + sub-
* @param {ObjectId} _id 公司id
*/
cashFN: async function(money, auth, type = 'cash', method = 'add', _id) {
try {
const {
compAccount,
compName
} = await this.findOne({
'_id': _id ? _id : auth.id
});
const nowCash = new Decimal(compAccount[type])[method](new Decimal(money)).toNumber();
let result;
if (type = 'cash') {
result = await this.update({
'_id': _id ? _id : auth.id
}, {
'compAccount.cash': nowCash
});
} else if (type = 'agent') {
result = await this.update({
'_id': _id ? _id : auth.id
}, {
'compAccount.agent': nowCash
});
} else {
result = await this.update({
'_id': _id ? _id : auth.id
}, {
'compAccount.other': nowCash
});
};
if (auth.group === 1) await this.cashWarning(ctx, _id);
return result.ok === 1 ? `${compName}${method === 'add' ? `充值¥:${money}元成功!` : '购买成功'}` : `${compName}充值失败`;
} catch (error) {
return error
}
},
// 余额预警
cashWarning: async function (ctx, _id) {
const {
compAccount,
compAccountWarnig,
compName
} = await this.findOne({
_id
});
const cashCount = Object.values(compAccount).reduce((a, b) => a + b);
if (compAccountWarnig >= cashCount) return await ctx.helper.sendWarning(ctx, `您的现金账户余额不足¥:${compAccountWarnig},为了保证您业务的正常运行,请及时充值!`, _id, compName);
},
// 登陆获取token
isLogin: async function (ctx) {
return await ctx.service.mongo.adcq.isLogin(ctx, 'Company', 'compPassword', async (e) => {
let {
_id,
compPhone,
compEmail,
compName,
compAuth,
compService,
updated,
compApi
} = e;
const ProjectID = (await ctx.model.Openstack.Identity.User.getObjectID(_id)).default_project_id;
try {
const TOKEN = await ctx.service.apps.jwt.sign({
id: _id,
group: 1,
phone: compPhone,
compApi,
ProjectID
});
// await ctx.model.Token.addOne({ authToken: TOKEN, updateTime: new Date() });
return {
token: TOKEN,
info: {
_id,
compPhone,
compEmail,
compName,
compAuth,
compService,
updated,
compApi
}
}
} catch (error) {
return error
}
})
},
// ak/sk生成token
apiToken: async function (body) {
const {
uid,
api_key,
secret_key
} = body;
const userInfo = await this.findOne({
_id: uid
}, {
'compASK': 1,
'compApi': 1,
});
const isHave = userInfo.compASK.filter(e => e.ak == api_key && e.sk == secret_key).length;
if (isHave === 1) {
let {
_id,
compApi
} = userInfo;
return {
data: {
token: await ctx.service.apps.jwt.sign({
id: _id,
group: 3,
service: compApi,
}),
expiresIn: 60 * 60 * 24 * 30 * 1000
}
}
} else {
return {
code: 422,
message: '参数不正确'
}
}
},
// 获取ask 列表
getASK: async function (_id) {
try {
const result = await this.findOne(_id);
return {
data: result.compASK
}
} catch (error) {}
},
// 创建ask
createASK: async function (ctx) {
if ((await this.getASK({
'_id': ctx.state.user.id
})).data.length === 20) return {
message: '每一用户只允许创建20个ak/sk序列对!',
code: 422
};
const asks = {
_id: mongoose.Types.ObjectId(),
info: '',
ak: crypto.createHash('md5').update(`${Math.random().toString(36).slice(2, 5)}`).digest('hex'),
sk: crypto.createHash('md5').update(`${Math.random().toString(36).slice(2, 8)}`).digest('hex'),
createTime: new Date()
};
return {
data: (await this.findOneAndUpdate({
'_id': ctx.state.user.id
}, {
$push: {
'compASK': asks
}
})).compASK.pop()
}
},
// 设置ask 名称
setInfo: async function (ctx) {
const {
info
} = ctx.request.body;
const result = await this.update({
'_id': ctx.params.id,
'compASK._id': mongoose.Types.ObjectId(ctx.request.body.id)
}, {
'$set': {
'compASK.$.info': info
}
});
return result.n === 1 && {
data: {
_id: ctx.params.id,
info: info
}
}
},
// 删除ak
delASK: async function name(ctx) {
const result = await this.update({
'_id': ctx.state.user.id
}, {
"$pull": {
'compASK': {
'_id': mongoose.Types.ObjectId(ctx.params.id)
}
}
});
return result.n === 1 && {
data: ctx.params.id
};
},
// 锁定公司
lockfn: async function (ctx, type) {
return {
data: await this.findOneAndUpdate({
'_id': ctx.params.id
}, {
$set: {
'status': 2
}
})
}
},
// 是否已存在
isIn: async function (params) {
return await this.findOne({ ...params
})
},
// 获取公司详细
getComp: async function (auth) {
return (await this.findOne({ ...(!isAdmin(auth) && {
'_id': auth.id
})
}))
},
// 获取现金
getCash: async function (auth) {
return (await this.getComp(auth)).compAccount
},
// 获取优惠券
getCoupon: async function (auth) {
const coupon = (await this.getComp(auth)).compCoupon;
return {
coupon: coupon,
length: coupon.length
}
},
// 获取服务
getService: async function (auth) {
return (await this.getComp(auth)).compService
},
// 获取客户列表
getCustomerList: async function (auth) {
const List = (await this.findOne({
'_id': auth.id
})).compCustomer;
const result = await this.find({
_id: {
$in: List
}
}, 'compName compEmail compPhone created');
return result;
},
// 绑定邮箱
bindMail: async function (auth) {
const result = await this.update({
'_id': auth.id
}, {
'compAuth.emailAuth': true
}, {
upsert: true
});
return result.ok === 1 && true;
},
// 登陆注册找回密码 pipe
lrfAction: async function (body) {
const {
action,
compPhone: SmsPhone,
code: SmsCode
} = body;
delete body.action
if (action === 'changePsd') {
const isInUsers = await this.isIn({
'compPhone': body.compPhone
});
if (!isInUsers) return {
message: '对不起您还不是我们的客户,请先注册!'
};
}
const checkCode = await ctx.model.SmsCode.check({
SmsPhone,
SmsCode
});
delete body.code
if (checkCode !== true) return checkCode;
return this[action](body);
},
// 注册
regist: async function (body) {
const {
compEmail
} = body;
try {
const result = await this.create(body);
if (result._id) {
await ctx.service.sms.mail.sendMail({
to: compEmail,
html: 'regist',
});
await ctx.model.System.Ui.User.create({
'_comp': result._id
});
const datas = await ctx.model.Openstack.Identity.User.createUser(result._id);
return {
code: 201,
data: {
name: result._id,
}
}
};
} catch (error) {
return error
}
},
// 修改密码
changePsd: async function (body) {
const {
compPhone,
compPassword
} = body;
return await this.findOneAndUpdate({
'compPhone': compPhone
}, {
'compPassword': compPassword
})
},
// 绑定客户
bindCustomer: async function (_id, auth) {
return await this.findOneAndUpdate({
_id
}, {
$push: {
'compCustomer': auth.id
}
})
},
}
return mongoose.model('Company', CompanySchema);
}
控制器
/*************************************************************
*
*- Copyright (c) qiduo, 2018
*- FileName: USERController.js 用户基本信息相关
*- Author: 罗波 lopo1983@vip.qq.com
*- Version: 1.0
*- Date:2018-10-15
*- Descripttion:openstack geecp
*- Modules:
*
*- Extends
* - 1.egg_Controller
*----------------------------------------------------------
*
*- Function List :
*
*- StaticsList :
* - index 获取用户列表
* - isHave 检测用户是否存在
* - show 获取单一用户信息
* - create 用户注册
*
*- History :
* <Author> <Date> <Desc>
*
**************************************************************/
'use strict';

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

class USERController 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 获取用户列表
*
* @description Admin 用户可用 可使用queryParamFn 进行查询
*
* @example GET /user/
*
*/
async index() {
const ctx = this.ctx;
const { helper } = ctx;
const result = await this.MODEL.getAll(helper.queryParamFn(ctx.query));
ctx.body = result
}
/**
*
* @name 检测用户是否存在
*
* @param {String} compName 用户名称
* @param {String} compEmail 用户邮箱
* @param {String} compPhone 用户手机
*
* @description compName, compEmail, compPhone 三选一 或可多选
*
* @example GET /user/isIn/?@param = param
*
*/
async isHave() {
const ctx = this.ctx;
const { compName, compEmail, compPhone } = ctx.query;
const result = (await this.MODEL.isIn(this.DUFn({ compName, compEmail, compPhone }))) === null;
ctx.body = { data: `${!result}` }
}
/**
*
* @name 获取用户信息
* @param {ObjectId} id 必传 ctx.params.id 用户id
*
* @example GET /user/{id}
*/
async show() {
const ctx = this.ctx;
const result = await this.MODEL.getOne({ '_id': ctx.params.id });
ctx.body = result
}
/**
*
* @name 用户注册 body = ctx.request.body
* @param {String} action 必传 动作 regist changePsd
* @param {String} compEmail 必传 邮箱
* @param {String} compPhone 必传 手机号码
* @param {String} compPassword 必传 用户密码 `${SALT}${MD5}`
* @param {String} code 必传 手机验证码,120s次 mongodb.smscodes expiresIn_TTL_60*3
*
* @example POST /user/
*
* @description
* - 邮箱和手机均能登陆
* - compEmail, compPhone, compPassword, action, code 均为必选参数
*
*/
async create() {
const { ctx } = this;
const { compEmail, compPhone, compPassword, action, code } = ctx.request.body;
const result = await this.MODEL.lrfAction({ compEmail, compPhone, compPassword, action, code });
ctx.body = result
}
/**
* @name 修改用户信息
*
* @param {String} action 必传 修改的方法
* @param {action}
* - changePsd 修改密码
* - bindCustomer 绑定客户
* - lrfAction 找回密码
* -
*
* @example PUT /user/{ObjecId}
*/
async update() {
const { ctx } = this;
const bodys = ctx.request.body;
const { action } = bodys;
delete bodys.action;
const result = await this.MODEL[action](bodys)
}
}

module.exports = USERController;

二 管理员用户
表设计
const crypto = require('crypto');
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const AdminSchema = new Schema({
// 用户名
adminName: {
type: String,
required: true,
unique: [true, '用户名已存在']
},
// 用户密码
adminPassword: {
type: String,
required: true,
set: v => {
let pilipala = Math.random().toString(36).slice(2, 8);
let bilibole = crypto.createHash('md5').update(`${v}:${pilipala}`).digest('hex');
return `${pilipala}${bilibole}`;
}
},
// 用户权限组
adminGroup: {
type: Number,
required: true,
default: 2
},
// 用户真实姓名
adminRealName: {
type: String
},
// 用户头像
adminHeader: {
type: String,
default: ''
},
adminStaff: {
// type: Schema.Types.ObjectId,
// ref: 'Staff',
},
status: {
type: Number,
default: 1
},
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
AdminSchema.statics = {
isLogin: async function (ctx) {
return await ctx.service.mongo.adcq.isLogin(ctx, 'Admin', 'adminPassword', async (e) => {
let { _id, adminRealName, adminName, adminGroup, adminHeader, adminStaff } = e;
return {
token: await ctx.service.apps.jwt.sign({ id: _id, group: adminGroup }),
info: { _id, adminRealName, adminName, adminHeader, adminStaff }
}
})
}
}
return mongoose.model('Admin', AdminSchema)
}
OpenStack 用户
表设计
const crypto = require('crypto');
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const { ctx, helper, service } = app.createAnonymousContext();
const OSIdentityUserSchema = new Schema({
// The user ID.
id: String,
// The ID of the default project for the user.
default_project_id: String,
// user description
description: String,
// The ID of the domain.
domain_id: String,
// user emaill address
email: String,
// If the user is enabled, this value is true. If the user is disabled, this value is false.
enabled: Boolean,
// The links for the user resource.
links: {},
// user name
name: String,
// 密码
password: String,
// 角色ID
role: String,
// 同步状态
sync: Boolean
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
OSIdentityUserSchema.statics = {
createUser: async function(_id) {
const setPSDFn = (v = _id) => {
let pilipala = Math.random().toString(36).slice(2, 8);
let bilibole = crypto.createHash('md5').update(`${v}:${pilipala}`).digest('hex');
return `${pilipala}${bilibole}`;
};
const password = setPSDFn();
try {
/****************************创建Project*********************************/
const PROJECT = await service.openstack.identity.projects.create({
name: _id,
'description': `comp_id:${_id} date:${new Date() -0 }`
});
const default_project_id = PROJECT.data.project.id;
// || PROJECT.data.error.code === 409
if (PROJECT.data.project) {
const DATAS = await service.openstack.identity.users.create({
'name': _id,
password,
default_project_id,
'description': `comp_id:${_id} date:${new Date()-0 }`
});
/******************************本地创建*******************************/
const result = await this.create({
...DATAS.data.user,
password,
default_project_id,
sync: true
});
/******************************分配角色*******************************/
if (!result.id) return;
await service.openstack.identity.roles.update(result.default_project_id, result.id, app.config.openstack.rolesID.default);
return result
}
} catch (error) {
return error
}
},
/**
* @name 获取一个
* @param {*} _id compid
* @param {*} select 选择的字段(BSON name/显示的名称 -name/不显示的名称)
*/
getOne: async function(_id, select) {
const MODEL = this.findOne({ name: _id }, !!select && select);
try {
const result = await MODEL;
return !!result ? result : await this.createUser(_id);
} catch (error) {
return error
}
},
// 获取ObjectID
getObjectID: async function(_id) {
const MODEL = this.findOne({ 'name': _id }, 'default_project_id -_id')
try {
return await MODEL
} catch (error) {
return error
}
}
}
return mongoose.model('openstack_Identity_user', OSIdentityUserSchema)
}
service?
role
'use strict';
const ServerIndex = require('../index')
//
class identityRolesService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.actions = ':5000/identity/v3/'
};
// 获取Region列表
async list(parent_region_id) {
try {
const datas = await this.OSAJax(`${this.actions}`, { body: { parent_region_id } })
return {
data: {
result: datas.regions,
totalCount: datas.regions.length
}
};
} catch (error) {
return {
data: error
}
}

}
// 显示regions详情
async show(project_id, user_id) {
let datas
try {
datas = await this.OSAJax(`${this.actions}projects/${project_id}/users/${user_id}/roles`)
} catch (error) {
datas = error
}
return datas
}
// 创建regions
async create(bodys) {
}
// 给用户赋予角色
async update(project_id, user_id, role_id) {
let datas;
try {
datas = await this.OSAJax(`${this.actions}projects/${project_id}/users/${user_id}/roles/${role_id}`, { method: 'PUT' });
} catch (error) {
datas = error
}
return { data: datas }
}
// 删除regions
async destroy(id) {
}
}
module.exports = identityRolesService;
project
'use strict';
const ServerIndex = require('../index')
//
class identityProjectService extends ServerIndex {
constructor(ctx) {
super(ctx);
this.actions = ':5000/identity/v3/projects'
};
// 获取Projects列表
async list() {
const datas = await this.OSAJax(`${this.actions}`);
return {
data: {
'result': datas.projects,
'totalCount': datas.projects.length
}
};
}
// 显示Projects详情
async show(id) {
const datas = await this.OSAJax(`${this.actions}/${id}`);
return { data: datas };
}
// 创建Projects
async create(bodys) {
const datas = await this.OSAJax(this.actions, { body: { 'project': bodys }, method: 'POST' });
return { data: datas }
}
// 修改Projects
async update(id, bodys) {
const datas = await this.OSAJax(`${this.actions}/${id}`, { body: { 'project': bodys }, method: 'PATCH' });
return { data: datas }
}
// 删除Projects
async destroy(id) {
const datas = await this.OSAJax(`${this.actions}/${id}`, { method: 'DELETE' });
return { data: datas }
}
}
module.exports = identityProjectService;
控制器
'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;
共用service
'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) : false);
method !== 'DELETE' && Object.assign(opt.data, {...!!body ? body : ''
});
// console.log(opt)
// console.log({
// // 'getFullRES': full,
// // 'isAdmin': !!_ucid ? `No | _ucid:${_ucid}` : 'Yes',
// 'URI': `http://${this.uri}${!!params ? params : ''}`,
// // 'Method': method,
// // ...method !== 'DELETE' && {
// // 'Body': opt.data
// // },
// // '_': new Date()
// })
try {
const result = await ctx.curl(`${this.uri}${!!params ? params : ''}`, opt);
return !!full && result || !!status && result.status || result.data;
} catch (error) {
return error
}
};
// 获取普通用户token
async getUserToken(_id) {
const ctx = this.ctx;
return (await ctx.model.Openstack.Token.getToken(_id)).data
};
// 获取普通用户projectID
async getProject(_id) {
const ctx = this.ctx;
return _id !== 'admin' ? (await this.OPUserMODEL.getOne(_id, 'default_project_id'))['default_project_id'] : this.config.openstack.projectID.default
};
// 从支付订单创建相关实例
/**
*
* @param {*} _comp 公司id
* @param {*} model 数据库
* @param {*} bodys 内容
* +@param {String} _metadata 关联数据
* @param {*} time 开通时长
*/
async createFromOrder(_comp, model, bodys, time, payment) {
try {
const ctx = this.ctx;
const MODELS = ctx.model.Openstack;
const {
deleteUndefined: DUFn,
subtractMoment: TMFn
} = ctx.helper
let datas = {}
if (model === 'cds') {
const {
availability_zone,
snapshot_id,
backup_id,
imageRef,
size,
volume_type,
_metadata
} = bodys;
const BODYS = DUFn({
availability_zone,
snapshot_id,
backup_id,
imageRef,
size,
volume_type,
_metadata
});
datas = await MODELS.BlcokStorage.Volumes.createOne(
_comp, {
...BODYS,
'endTime': TMFn(time, 'month', 'add'),
payment
}
);
}
if (model === 'ecs') {
const {
availability_zone,
flavorRef,
name,
uuid,
imageRef,
server_password,
key_name,
security_groups,
_metadata
} = bodys;
const SERVERBODYS = DUFn({
availability_zone,
flavorRef,
name,
uuid,
imageRef,
server_password,
key_name,
security_groups,
_metadata
});
datas = await MODELS.Servers.Server.createServer(
_comp, {
...SERVERBODYS,
'endTime': TMFn(time, 'month', 'add'),
payment,
}
);
}
if (model === 'eip') {
const {
description,
_metadata
} = bodys;
const EIPBODYS = DUFn({
description,
_metadata
});
datas = await MODELS.Neutron.FloatingIP.createOne(
_comp, {
...EIPBODYS,
'endTime': TMFn(time, 'month', 'add'),
payment
})
}
return datas
} catch (error) {
console.log(error)
}
}
}
module.exports = indexService;

egg.js+mongodb+openstack 公有云计费系统(一)

lopo1983 发表了文章 • 0 个评论 • 1048 次浏览 • 2019-03-13 23:58 • 来自相关话题

本代码仅供学习 参考用 请勿做其他用途
?本项目结构
?
用户系统财务系统工单系统计费系统OpenStack (rocky)
?
OpenStack 功能
用户系统identity磁盘blcokStorage计算compute监控gnocchi网络 network
?
所需依赖
"dependencies": {
"egg": "^2.2.1",
"egg-scripts": "^2.5.0",
"lodash": "^4.17.11",
"xml2js": "^0.4.19"
},
"devDependencies": {
"autod": "^3.0.1",
"autod-egg": "^1.0.0",
"baidu-aip-sdk": "^2.3.3",
"bce-sdk-js": "^0.2.9",
"decimal": "0.0.2",
"decimal.js": "^10.0.1",
"egg-bin": "^4.3.5",
"egg-ci": "^1.8.0",
"egg-cors": "^2.1.0",
"egg-jwt": "^3.1.2",
"egg-mock": "^3.14.0",
"egg-mongoose": "^3.1.0",
"egg-multipart": "^2.1.0",
"egg-validate": "^1.1.1",
"egg-wechat-api": "^1.2.2",
"eslint": "^4.11.0",
"eslint-config-egg": "^6.0.0",
"formstream": "^1.1.0",
"jpush-async": "^4.0.0-rc.1",
"koa-useragent": "^1.1.0",
"moment": "^2.22.2",
"nodemailer": "^4.6.8",
"request": "^2.88.0",
"request-promise-native": "^1.0.5",
"stream-to-array": "^2.3.0",
"stream-wormhole": "^1.1.0",
"webstorm-disable-index": "^1.2.0"
},
环境?
node 8.11?
mongodb4.x?
openstack(rocky)
?
注意:
?
1.本教程不会上传相关代码到GitHub ;
2.本教程需要熟悉egg.js mongoose ES6/7;
3.熟悉Async/await
? 查看全部

本代码仅供学习 参考用 请勿做其他用途


?本项目结构
?
  • 用户系统
  • 财务系统
  • 工单系统
  • 计费系统
  • OpenStack (rocky)

  • ?
    OpenStack 功能
  • 用户系统identity
  • 磁盘blcokStorage
  • 计算compute
  • 监控gnocchi
  • 网络 network

  • ?
    所需依赖
      "dependencies": {
    "egg": "^2.2.1",
    "egg-scripts": "^2.5.0",
    "lodash": "^4.17.11",
    "xml2js": "^0.4.19"
    },
    "devDependencies": {
    "autod": "^3.0.1",
    "autod-egg": "^1.0.0",
    "baidu-aip-sdk": "^2.3.3",
    "bce-sdk-js": "^0.2.9",
    "decimal": "0.0.2",
    "decimal.js": "^10.0.1",
    "egg-bin": "^4.3.5",
    "egg-ci": "^1.8.0",
    "egg-cors": "^2.1.0",
    "egg-jwt": "^3.1.2",
    "egg-mock": "^3.14.0",
    "egg-mongoose": "^3.1.0",
    "egg-multipart": "^2.1.0",
    "egg-validate": "^1.1.1",
    "egg-wechat-api": "^1.2.2",
    "eslint": "^4.11.0",
    "eslint-config-egg": "^6.0.0",
    "formstream": "^1.1.0",
    "jpush-async": "^4.0.0-rc.1",
    "koa-useragent": "^1.1.0",
    "moment": "^2.22.2",
    "nodemailer": "^4.6.8",
    "request": "^2.88.0",
    "request-promise-native": "^1.0.5",
    "stream-to-array": "^2.3.0",
    "stream-wormhole": "^1.1.0",
    "webstorm-disable-index": "^1.2.0"
    },

    环境?
    node 8.11?
    mongodb4.x?
    openstack(rocky)
    ?
    注意:
    ?
    1.本教程不会上传相关代码到GitHub ;
    2.本教程需要熟悉egg.js mongoose ES6/7;
    3.熟悉Async/await
    ?

    12306 查询余票

    lopo1983 发表了文章 • 0 个评论 • 1136 次浏览 • 2019-01-20 18:02 • 来自相关话题

    以下代码仅供学习交流用,切勿用作其他用途
    /data/favorite_name
    见附件
    index.js
    const DATAS = require('../12306/data/favorite_name');
    const request = require('request-promise-native');
    const STATIONS = DATAS.split('|');
    const split_array = (arr, len) => {
    let a_len = arr.length;
    let result = [];
    for (let i = 0; i < a_len; i += len) {
    result.push(arr.slice(i, i + len));
    }
    return result;
    }
    const STATIONSMap = split_array(STATIONS, 5).reduce((a, b) => {
    a.push({
    code: b[0],
    name: b[1],
    station: b[2],
    station_py: b[3],
    station_pyj: b[4]
    });
    return a
    }, []);
    const getStation = (name, key = 'name') => STATIONSMap.filter(e => e.station == name)[0][key]
    // console.log(STATIONS.filter(e => e.station == 'HJL')[0].name)
    const options = {
    headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36' },
    uri: 'https://kyfw.12306.cn/otn/leftTicket/queryZ',
    qs: {
    'leftTicketDTO.train_date': '2019-01-28',
    'leftTicketDTO.from_station': 'SRH',
    'leftTicketDTO.to_station': 'DYW',
    'purpose_codes': 'ADULT'
    },
    transform: e => JSON.parse(e).data.result.reduce((a, b) => {
    const arr = b.split('|');
    a.push({
    remark: arr[1], //备注
    _id: arr[2], //id
    type: arr[3].slice(0, 1), //车次类型
    number: arr[3], //车号
    station_begin: getStation(arr[4]), //起点站
    station_end: getStation(arr[5]), //终点站
    station_from: getStation(arr[6]), //出发站
    station_to: getStation(arr[7]), //到达站
    time_go: arr[8], //出发时间
    time_arrival: arr[9], //到达时间
    time_take: arr[10], //历时
    O: arr[32], //二等座
    M: arr[31], //一等座
    A9: arr[30], //商务特等座
    });
    return a
    }, [])
    };
    !(async() => {
    try {
    const datas = await request(options);
    console.log(datas)
    } catch (error) {
    console.log(error)
    }
    })() 查看全部

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


    /data/favorite_name
    见附件
    index.js
    const DATAS = require('../12306/data/favorite_name');
    const request = require('request-promise-native');
    const STATIONS = DATAS.split('|');
    const split_array = (arr, len) => {
    let a_len = arr.length;
    let result = [];
    for (let i = 0; i < a_len; i += len) {
    result.push(arr.slice(i, i + len));
    }
    return result;
    }
    const STATIONSMap = split_array(STATIONS, 5).reduce((a, b) => {
    a.push({
    code: b[0],
    name: b[1],
    station: b[2],
    station_py: b[3],
    station_pyj: b[4]
    });
    return a
    }, []);
    const getStation = (name, key = 'name') => STATIONSMap.filter(e => e.station == name)[0][key]
    // console.log(STATIONS.filter(e => e.station == 'HJL')[0].name)
    const options = {
    headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36' },
    uri: 'https://kyfw.12306.cn/otn/leftTicket/queryZ',
    qs: {
    'leftTicketDTO.train_date': '2019-01-28',
    'leftTicketDTO.from_station': 'SRH',
    'leftTicketDTO.to_station': 'DYW',
    'purpose_codes': 'ADULT'
    },
    transform: e => JSON.parse(e).data.result.reduce((a, b) => {
    const arr = b.split('|');
    a.push({
    remark: arr[1], //备注
    _id: arr[2], //id
    type: arr[3].slice(0, 1), //车次类型
    number: arr[3], //车号
    station_begin: getStation(arr[4]), //起点站
    station_end: getStation(arr[5]), //终点站
    station_from: getStation(arr[6]), //出发站
    station_to: getStation(arr[7]), //到达站
    time_go: arr[8], //出发时间
    time_arrival: arr[9], //到达时间
    time_take: arr[10], //历时
    O: arr[32], //二等座
    M: arr[31], //一等座
    A9: arr[30], //商务特等座
    });
    return a
    }, [])
    };
    !(async() => {
    try {
    const datas = await request(options);
    console.log(datas)
    } catch (error) {
    console.log(error)
    }
    })()

    baidu sdk 鉴权处理(nodejs)

    lopo1983 发表了文章 • 0 个评论 • 1022 次浏览 • 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中国图片

    lopo1983 发表了文章 • 0 个评论 • 1037 次浏览 • 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编写股票数据爬虫

    lopo1983 发表了文章 • 0 个评论 • 1350 次浏览 • 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', 'FPutf-8I', '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', 'FPutf-8I', '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函数(持续更新)

    lopo1983 发表了文章 • 0 个评论 • 1138 次浏览 • 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`)
    }
    };