init
This commit is contained in:
commit
b978b16e6a
22
.gitignore
vendored
Normal file
22
.gitignore
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
/.env
|
||||
/.idea/
|
||||
|
||||
**/node_modules
|
||||
**/.DS_Store
|
||||
|
||||
/config/config.js
|
||||
/config/game_dic.js
|
||||
|
||||
/public
|
||||
/logs
|
||||
/build
|
||||
/dist
|
||||
/lib
|
||||
rev-manifest.json
|
||||
/yarn.lock
|
||||
/nohup.out
|
||||
/package-lock.json
|
||||
/dist.tar.gz
|
||||
*.swp
|
||||
/logsgarfield
|
||||
.vscode/launch.json
|
42
package.json
Normal file
42
package.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "admin-be",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dev": "cross-env nodemon src/app.js --exec babel-node "
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"bluebird": "^3.5.4",
|
||||
"body-parser": "^1.19.0",
|
||||
"bunyan": "^1.8.12",
|
||||
"compression": "^1.7.4",
|
||||
"connect-mongo": "^2.0.3",
|
||||
"cookie-parser": "^1.4.4",
|
||||
"express": "^4.16.4",
|
||||
"express-flash": "0.0.2",
|
||||
"express-session": "^1.16.1",
|
||||
"express-validator": "^5.3.1",
|
||||
"file-stream-rotator": "^0.4.1",
|
||||
"fs-extra": "^8.0.0",
|
||||
"glob": "^7.1.4",
|
||||
"helmet": "^3.18.0",
|
||||
"ldapjs": "^1.0.2",
|
||||
"method-override": "^3.0.0",
|
||||
"mongoose": "^5.5.7",
|
||||
"morgan": "^1.9.1",
|
||||
"node-schedule": "^1.3.2",
|
||||
"nodemon": "^1.19.0",
|
||||
"request": "^2.88.0",
|
||||
"serve-favicon": "^2.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"cross-env": "^5.2.0"
|
||||
}
|
||||
}
|
35
src/app.js
Normal file
35
src/app.js
Normal file
@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
import mongoose from 'mongoose';
|
||||
import config from '../config/config';
|
||||
import app from './bin/express';
|
||||
import glob from 'glob';
|
||||
import Promise from 'bluebird';
|
||||
import logger from './utils/logger';
|
||||
import http from 'http';
|
||||
import msgSchedule from './schedule/weappmsg.schedule';
|
||||
|
||||
mongoose.Promise = Promise;
|
||||
|
||||
const db = mongoose.connection;
|
||||
|
||||
db.on('error', function(err) {
|
||||
logger.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
db.once('open', function() {
|
||||
logger.info('Connected to db.');
|
||||
});
|
||||
mongoose.connect(config.db_admin, {promiseLibrary: Promise, useNewUrlParser: true});
|
||||
const models = glob.sync(config.root + './src/models/*.js');
|
||||
models.forEach(function(model) {
|
||||
require(model);
|
||||
});
|
||||
|
||||
|
||||
const server = http.createServer(app);
|
||||
|
||||
msgSchedule.scheduleSendAll();
|
||||
|
||||
server.listen(config.port, function() {
|
||||
logger.info(`${config.app.name} garfield server listening on port ${config.port}`);
|
||||
});
|
183
src/bin/express.js
Normal file
183
src/bin/express.js
Normal file
@ -0,0 +1,183 @@
|
||||
'use strict';
|
||||
|
||||
import express from 'express';
|
||||
import expressValidator from 'express-validator';
|
||||
import flash from 'express-flash';
|
||||
import session from 'express-session';
|
||||
import bodyParser from 'body-parser';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import compress from 'compression';
|
||||
import methodOverride from 'method-override';
|
||||
import helmet from 'helmet';
|
||||
// import favicon from 'serve-favicon';
|
||||
import FileStreamRotator from 'file-stream-rotator';
|
||||
import morgan from 'morgan';
|
||||
import fs from 'fs';
|
||||
import logger from './../utils/logger'
|
||||
import expressUtils from './../utils/express-utils';
|
||||
import config from './../../config/config';
|
||||
import connectMongo from 'connect-mongo';
|
||||
import routes from './../router/index';
|
||||
|
||||
const app = express();
|
||||
const MongoStore = connectMongo(session);
|
||||
const env = process.env.NODE_ENV || 'development';
|
||||
const isDev = env === 'development';
|
||||
app.locals.ENV = env;
|
||||
app.locals.ENV_DEVELOPMENT = isDev;
|
||||
|
||||
app.use(helmet());
|
||||
|
||||
app.disable('x-powered-by');
|
||||
// app.use(favicon(config.root + '/public/img/favicon/favicon.ico'));
|
||||
|
||||
const logDir = config.logs_path;
|
||||
fs.existsSync(logDir) || fs.mkdirSync(logDir);
|
||||
|
||||
/**
|
||||
* 记录除favicon之外的所有请求
|
||||
*/
|
||||
const accessLogStream = FileStreamRotator.getStream({
|
||||
date_format: 'YYYYMMDD',
|
||||
filename: logDir + '/' + config.app.name + '-access-%DATE%.log',
|
||||
frequency: 'daily',
|
||||
verbose: false
|
||||
});
|
||||
/**
|
||||
* 仅记录失败的请求
|
||||
*/
|
||||
const errorLogStream = FileStreamRotator.getStream({
|
||||
date_format: 'YYYYMMDD',
|
||||
filename: logDir + '/' + config.app.name + '-error-%DATE%.log',
|
||||
frequency: 'daily',
|
||||
verbose: false
|
||||
});
|
||||
/**
|
||||
* 记录非静态文件请求
|
||||
*/
|
||||
const requestLogStream = FileStreamRotator.getStream({
|
||||
date_format: 'YYYYMMDD',
|
||||
filename: logDir + '/' + config.app.name + '-request-%DATE%.log',
|
||||
frequency: 'daily',
|
||||
verbose: false
|
||||
});
|
||||
|
||||
morgan.token('remote-addr', function(req, res) {
|
||||
const ip =
|
||||
req.headers['x-real-ip'] ||
|
||||
req.ip ||
|
||||
req._remoteAddress ||
|
||||
(req.connection && req.connection.remoteAddress) ||
|
||||
undefined;
|
||||
req._remoteAddress = ip;
|
||||
return ip;
|
||||
});
|
||||
|
||||
if (isDev) {
|
||||
// app.use(morgan('dev' ));
|
||||
}
|
||||
app.use(
|
||||
morgan('combined', {
|
||||
stream: accessLogStream,
|
||||
skip: function(req, res) {
|
||||
return req.method === 'HEAD';
|
||||
}
|
||||
})
|
||||
);
|
||||
app.use(
|
||||
morgan('combined', {
|
||||
stream: errorLogStream,
|
||||
skip: function(req, res) {
|
||||
return res.statusCode < 400;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
app.get('/robots.txt', function(req, res) {
|
||||
res.type('text/plain');
|
||||
res.send('User-agent: *\nDisallow: /agent/');
|
||||
});
|
||||
|
||||
// -- We don't want to serve sessions for static resources
|
||||
// -- Save database write on every resources
|
||||
app.use(compress());
|
||||
app.use(express.static(config.root + '/public'));
|
||||
|
||||
app.use(
|
||||
morgan('combined', {
|
||||
stream: requestLogStream,
|
||||
skip: function(req, res) {
|
||||
return req.method === 'HEAD';
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
app.use(bodyParser.json({ limit: '5mb' }));
|
||||
app.use(
|
||||
bodyParser.urlencoded({
|
||||
limit: '5mb',
|
||||
parameterLimit: 50000,
|
||||
extended: true
|
||||
})
|
||||
);
|
||||
app.use(expressValidator());
|
||||
app.use(cookieParser(config.secret));
|
||||
app.use(
|
||||
session({
|
||||
secret: config.secret,
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
name: config.session_name,
|
||||
store: new MongoStore({
|
||||
url: config.db_admin,
|
||||
autoReconnect: true,
|
||||
collection: 'garfield_sessions'
|
||||
})
|
||||
})
|
||||
);
|
||||
app.use(flash());
|
||||
app.use(expressUtils());
|
||||
app.use(methodOverride());
|
||||
app.use(function(req, res, next) {
|
||||
res.locals.user = req.user;
|
||||
next();
|
||||
});
|
||||
|
||||
app.all('/uploads', function(req, res, next) {
|
||||
res.header('Access-Control-Allow-Origin', '*');
|
||||
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
|
||||
res.header('Access-Control-Allow-Headers', 'X-Requested-With');
|
||||
res.header('Access-Control-Allow-Headers', 'Content-Type');
|
||||
next();
|
||||
});
|
||||
app.use('/', routes);
|
||||
|
||||
app.use(function(req, res, next) {
|
||||
const err = new Error('未找到您要访问的页面 (´・_・`)');
|
||||
err.status = 404;
|
||||
// next(err);
|
||||
err.status = err.status || 500;
|
||||
if (err.status !== 404) {
|
||||
logger.error(err);
|
||||
}
|
||||
next(err);
|
||||
});
|
||||
app.use(function(err, req, res, next) {
|
||||
logger.error({
|
||||
method: req.method,
|
||||
path: req.path,
|
||||
err_status: err.status,
|
||||
err_message: err.message
|
||||
});
|
||||
if (req.path.startsWith('/api')) {
|
||||
res.json({ errcode: 10, errmsg: err.message });
|
||||
} else {
|
||||
res.render('error', {
|
||||
message: err.status === 404 ? err.message : '服务器君开小差啦 @(・●・)@',
|
||||
error: err,
|
||||
title: err.status
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default app;
|
11
src/controllers/common/index.js
Normal file
11
src/controllers/common/index.js
Normal file
@ -0,0 +1,11 @@
|
||||
import {Router} from 'express';
|
||||
|
||||
const router = new Router();
|
||||
|
||||
router.get('/test', async(req, res, next) => {
|
||||
res.send({
|
||||
msg: 'test!'
|
||||
})
|
||||
})
|
||||
|
||||
export default router;
|
149
src/models/admin/Admin.js
Normal file
149
src/models/admin/Admin.js
Normal file
@ -0,0 +1,149 @@
|
||||
'use strict';
|
||||
import mongoose from 'mongoose';
|
||||
import passportLocalMongoose from 'passport-local-mongoose';
|
||||
|
||||
|
||||
const AdminActionSchema = new mongoose.Schema({
|
||||
_id: {type: String, required: true},
|
||||
name: {type: String, required: true},
|
||||
paths: [{
|
||||
method: String,
|
||||
path: String,
|
||||
}],
|
||||
|
||||
deleted: {type: Boolean, default: false},
|
||||
deleted_time: {type: Date},
|
||||
}, {
|
||||
collection: 'admin_actions',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
const AdminAction = mongoose.model('AdminAction', AdminActionSchema);
|
||||
|
||||
const AdminRoleSchema = new mongoose.Schema({
|
||||
_id: {type: String, required: true},
|
||||
name: {type: String, required: true},
|
||||
permissions: [{type: String, ref: 'AdminAction'}],
|
||||
|
||||
deleted: {type: Boolean, default: false},
|
||||
deleted_time: {type: Date},
|
||||
}, {
|
||||
collection: 'admin_roles',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
const AdminRole = mongoose.model('AdminRole', AdminRoleSchema);
|
||||
|
||||
/**
|
||||
* 用户表不需要添加 timestamps: true
|
||||
*
|
||||
* 因为 createdAt 可以从ObjectID中提取, updatedAt 会因为last和ateemps随时变更,
|
||||
* 如果需要跟踪关键数据的变更时间, 应添加独立自管的 updatedAt,
|
||||
* 或者直接使用 immutable data 追踪变化
|
||||
*/
|
||||
const AdminSchema = new mongoose.Schema({
|
||||
username: String,
|
||||
password: String,
|
||||
|
||||
roles: [{type: String, ref: 'AdminRole'}],
|
||||
permissions: [{type: String, ref: 'AdminAction'}],
|
||||
games: [{type: String}],
|
||||
profile: {
|
||||
name: String,
|
||||
gender: String,
|
||||
},
|
||||
|
||||
comment: String,
|
||||
|
||||
createdBy: {type: String, ref: 'Admin', required: true},
|
||||
lastModifiedBy: {type: String, ref: 'Admin', required: true},
|
||||
|
||||
locked: {type: Boolean, default: false},
|
||||
locked_time: {type: Date},
|
||||
|
||||
deleted: {type: Boolean, default: false},
|
||||
delete_time: {type: Date},
|
||||
|
||||
lastLogin: Date, // passport-local-mongoose 添加的 last 字段记录的是最后一次登录尝试,
|
||||
// 而不是最后一次成功登录
|
||||
});
|
||||
|
||||
AdminSchema.virtual('createdAt').get(function() {
|
||||
return this._id.getTimestamp();
|
||||
});
|
||||
|
||||
/**
|
||||
* TODO this is far away from elegant, maybe change it later.
|
||||
*/
|
||||
AdminSchema.virtual('hasSysAdmin').get(function() {
|
||||
return this.roles.includes('sys_admin');
|
||||
});
|
||||
AdminSchema.virtual('hasNavSystem').get(function() {
|
||||
let yes = this.roles.includes('admin');
|
||||
yes = yes || this.permissions.includes('edit_accounts');
|
||||
return yes;
|
||||
});
|
||||
|
||||
AdminSchema.virtual('hasNavGameApi').get(function() {
|
||||
let yes = this.roles.includes('admin');
|
||||
yes = yes || this.roles.includes('game_api_manager');
|
||||
yes = yes || this.permissions.includes('edit_game_apis');
|
||||
return yes;
|
||||
});
|
||||
|
||||
AdminSchema.virtual('hasNavRedis').get(function() {
|
||||
let yes = this.roles.includes('admin');
|
||||
yes = yes || this.roles.includes('redis_manager');
|
||||
yes = yes || this.permissions.includes('edit_redis');
|
||||
return yes;
|
||||
});
|
||||
|
||||
AdminSchema.virtual('hasNavWechat').get(function() {
|
||||
let yes = this.roles.includes('admin');
|
||||
yes = yes || this.roles.includes('wechat_manager');
|
||||
yes = yes || this.permissions.includes('edit_wechat');
|
||||
return yes;
|
||||
});
|
||||
|
||||
AdminSchema.virtual('hasNavGM').get(function() {
|
||||
let yes = this.roles.includes('admin');
|
||||
yes = yes || this.roles.includes('gm_manager');
|
||||
yes = yes || this.permissions.includes('edit_gm');
|
||||
return yes;
|
||||
});
|
||||
|
||||
AdminSchema.virtual('hasNavTool').get(function() {
|
||||
let yes = this.roles.includes('admin');
|
||||
yes = yes || this.roles.includes('tool_manager');
|
||||
yes = yes || this.permissions.includes('edit_tool');
|
||||
return yes;
|
||||
});
|
||||
|
||||
AdminSchema.virtual('hasAdminRole').get(function() {
|
||||
return this.roles.includes('admin');
|
||||
});
|
||||
|
||||
AdminSchema.methods.checkGame = function(gid) {
|
||||
let yes = this.roles.includes('admin');
|
||||
yes = yes || this.games.includes(gid);
|
||||
return yes;
|
||||
};
|
||||
|
||||
AdminSchema.plugin(passportLocalMongoose,
|
||||
{
|
||||
limitAttempts: true,
|
||||
usernameLowerCase: true,
|
||||
maxAttempts: 10,
|
||||
errorMessages: {
|
||||
MissingPasswordError: '请输入密码',
|
||||
AttemptTooSoonError: '您的账户当前被锁定,请稍后再试',
|
||||
TooManyAttemptsError: '您的账户因错误登录次数太多而被锁定',
|
||||
NoSaltValueStoredError: 'Authentication not possible. No salt value stored',
|
||||
IncorrectPasswordError: '用户名或密码错误',
|
||||
IncorrectUsernameError: '用户名或密码错误',
|
||||
MissingUsernameError: '请输入用户名',
|
||||
UserExistsError: '您输入的用户名已被使用',
|
||||
},
|
||||
});
|
||||
const Admin = mongoose.model('Admin', AdminSchema);
|
||||
export {Admin, AdminRole, AdminAction};
|
31
src/models/admin/AdminLog.js
Normal file
31
src/models/admin/AdminLog.js
Normal file
@ -0,0 +1,31 @@
|
||||
'use strict';
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
const ObjectId = Schema.Types.ObjectId;
|
||||
/**
|
||||
* 操作日志
|
||||
*/
|
||||
const AdminLog = new mongoose.Schema({
|
||||
// 游戏id
|
||||
admin: {type: ObjectId, ref: 'Admin'},
|
||||
username: {type: String},
|
||||
method: {type: String},
|
||||
show_name: {type: String},
|
||||
// 请求路径
|
||||
path: {type: String},
|
||||
user_agent: {type: String},
|
||||
referer: {type: String},
|
||||
// 请求的param
|
||||
params: {type: Schema.Types.Mixed},
|
||||
// ip
|
||||
ip: {type: String},
|
||||
// 备注
|
||||
comment: {type: String},
|
||||
}, {
|
||||
collection: 'admin_logs',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
export default mongoose.model('AdminLog', AdminLog);
|
||||
|
30
src/models/admin/Game.js
Normal file
30
src/models/admin/Game.js
Normal file
@ -0,0 +1,30 @@
|
||||
'use strict';
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
/**
|
||||
* 游戏信息
|
||||
*/
|
||||
const Game = new mongoose.Schema({
|
||||
// 游戏id
|
||||
game_id: {type: String},
|
||||
// 游戏名
|
||||
name: {type: String},
|
||||
// 英文名
|
||||
name_en: {type: String},
|
||||
// 状态
|
||||
status: {type: String},
|
||||
// 平台
|
||||
platform: {type: String},
|
||||
// 备注
|
||||
comment: {type: String},
|
||||
createdBy: {type: String, ref: 'Admin'},
|
||||
lastModifiedBy: {type: String, ref: 'Admin'},
|
||||
deleted: {type: Boolean, default: false},
|
||||
delete_time: {type: Date},
|
||||
}, {
|
||||
collection: 'games',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
export default mongoose.model('Game', Game);
|
||||
|
106
src/models/admin/SystemDic.js
Normal file
106
src/models/admin/SystemDic.js
Normal file
@ -0,0 +1,106 @@
|
||||
'use strict';
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
/**
|
||||
* 字典表
|
||||
*/
|
||||
const Schema = mongoose.Schema;
|
||||
|
||||
const SystemDicSchema = new mongoose.Schema({
|
||||
key: {type: String},
|
||||
value: {type: Schema.Types.Mixed},
|
||||
/**
|
||||
* 类型
|
||||
* system: 字典类型
|
||||
* platform: 平台
|
||||
* game_cfg: 游戏配置项目
|
||||
* share_cfg: 游戏分享类型
|
||||
* game_type: 游戏类型
|
||||
* game_status: 游戏状态
|
||||
* */
|
||||
type: {type: String},
|
||||
// 备注
|
||||
comment: {type: String},
|
||||
createdBy: {type: String, ref: 'Admin'},
|
||||
lastModifiedBy: {type: String, ref: 'Admin'},
|
||||
deleted: {type: Boolean, default: false},
|
||||
delete_time: {type: Date},
|
||||
deletedBy: {type: String, ref: 'Admin'},
|
||||
}, {
|
||||
collection: 'system_dics',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
/**
|
||||
* */
|
||||
class SystemDicClass {
|
||||
/**
|
||||
* 获取某一个类型的字典的Map
|
||||
* @param {string} type, 字典类型
|
||||
* @return {Promise} data
|
||||
* */
|
||||
static getDicMap(type) {
|
||||
return this.find({type: type, deleted: false})
|
||||
.then((records) => {
|
||||
const map = new Map();
|
||||
for (const record of records) {
|
||||
map.set(record.key, record.value);
|
||||
}
|
||||
return map;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 生成查询条件
|
||||
* @param {Object} req
|
||||
* @return {Object} opt
|
||||
* @return {Object} sortObj
|
||||
* */
|
||||
static generateQueryOpt(req) {
|
||||
let opt = {};
|
||||
const body = req.body;
|
||||
const order = body.order;
|
||||
const sort = body.sort ? body.sort : 'createdAt';
|
||||
const sortObj = {};
|
||||
sortObj[sort] = order === 'asc' ? 1 : -1;
|
||||
if (body.keyStr) {
|
||||
const orArr = [
|
||||
{key: {$regex: body.keyStr, $options: 'i'}},
|
||||
{value: {$regex: body.keyStr, $options: 'i'}},
|
||||
];
|
||||
opt = {$or: orArr};
|
||||
}
|
||||
if (body.type && body.type !== 'all') {
|
||||
opt.type = body.type;
|
||||
}
|
||||
opt.deleted = false;
|
||||
return {opt, sortObj};
|
||||
}
|
||||
/**
|
||||
* 保存
|
||||
* @param {Object} req
|
||||
* */
|
||||
static async saveWithReq(req) {
|
||||
const body = req.body;
|
||||
const data = body.data;
|
||||
const ignoreKeySet = new Set(['createdAt', 'updatedAt', '__v', 'deleted', 'delete_time', 'deletedBy']);
|
||||
let record;
|
||||
if (data._id) {
|
||||
record = await this.findById(data._id);
|
||||
}
|
||||
if (!record) {
|
||||
record = new SystemDicModel({
|
||||
createdBy: req.user.id,
|
||||
});
|
||||
}
|
||||
for (const key in body.data) {
|
||||
if ({}.hasOwnProperty.call(body.data, key) && !ignoreKeySet.has(key)) {
|
||||
record[key] = body.data[key];
|
||||
}
|
||||
}
|
||||
return record.save();
|
||||
}
|
||||
}
|
||||
SystemDicSchema.loadClass(SystemDicClass);
|
||||
|
||||
const SystemDicModel = mongoose.model('SystemDic', SystemDicSchema);
|
||||
export default SystemDicModel;
|
17
src/models/admin/TemplateMsgRecord.js
Normal file
17
src/models/admin/TemplateMsgRecord.js
Normal file
@ -0,0 +1,17 @@
|
||||
'use strict';
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
/**
|
||||
* 模版消息发送日志
|
||||
*/
|
||||
const TemplateMsgRecord = new mongoose.Schema({
|
||||
// 游戏id
|
||||
app_id: {type: String},
|
||||
open_ids: [{type: String}],
|
||||
}, {
|
||||
collection: 'template_msg_record',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
export default mongoose.model('TemplateMsgRecord', TemplateMsgRecord);
|
||||
|
50
src/models/beagle/WeappFormID.js
Normal file
50
src/models/beagle/WeappFormID.js
Normal file
@ -0,0 +1,50 @@
|
||||
'use strict';
|
||||
import mongoose from 'mongoose';
|
||||
const Schema = mongoose.Schema;
|
||||
import dbUtil from '../../utils/db.util';
|
||||
/**
|
||||
* 小程序的Form ID, 用于发送推送消息
|
||||
*/
|
||||
const WeappFormID = new Schema({
|
||||
account: {type: String},
|
||||
// 0: 未支付,1:已使用, -1:已过期
|
||||
status: {type: Number},
|
||||
app_id: {type: String},
|
||||
open_id: {type: String},
|
||||
form_id: {type: String},
|
||||
// 过期时间
|
||||
expire_time: {type: Date},
|
||||
// 发送时间
|
||||
send_time: {type: Date},
|
||||
}, {
|
||||
collection: 'weapp_form_id',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
const conn = dbUtil.getConnBeagle();
|
||||
|
||||
const WeappFormIDModel = conn.model('WeappFormID', WeappFormID);
|
||||
|
||||
// 获取当天可用的formId列表, 每个用户一条
|
||||
WeappFormIDModel.getUserRecordsDay = function(appId) {
|
||||
const idSet = new Set();
|
||||
return WeappFormIDModel
|
||||
.find({status: 0, expire_time: {$gt: new Date()}, app_id: appId})
|
||||
.sort({createdAt: 1})
|
||||
.then((records) => {
|
||||
const result = [];
|
||||
for (const record of records) {
|
||||
if (!idSet.has(record.open_id)) {
|
||||
idSet.add(record.open_id);
|
||||
result.push(record);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
};
|
||||
WeappFormIDModel.updateExpireTime = function() {
|
||||
return WeappFormIDModel.where({status: 0, expire_time: {$lte: new Date()}}).updateMany({status: -1}).exec();
|
||||
};
|
||||
export default WeappFormIDModel;
|
||||
|
||||
|
32
src/models/dalmatian/ShortUrl.js
Normal file
32
src/models/dalmatian/ShortUrl.js
Normal file
@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
import mongoose from 'mongoose';
|
||||
import shortid from 'shortid';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
/**
|
||||
* 短链接
|
||||
*/
|
||||
const ShortUrl = new mongoose.Schema({
|
||||
_id: {type: String, default: shortid.generate},
|
||||
// 类型,page类型跳转实际url,weapp类型的话,real_url存储宣传图
|
||||
type: {type: String, required: true, default: 'page'},
|
||||
// 真实链接
|
||||
real_url: {type: String},
|
||||
image_url: {type: String},
|
||||
data: {type: Schema.Types.Mixed},
|
||||
deleted: {type: Boolean, default: false},
|
||||
deletedBy: {type: String, ref: 'Admin'},
|
||||
delete_time: {type: Date},
|
||||
createdBy: {type: String, ref: 'Admin', required: true},
|
||||
lastModifiedBy: {type: String, ref: 'Admin', required: true},
|
||||
// 备注
|
||||
comment: {type: String},
|
||||
}, {
|
||||
collection: 'short_url',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
const conn = dbUtil.getConnDalmatian();
|
||||
|
||||
export default conn.model('ShortUrl', ShortUrl);
|
112
src/models/ghost/Account.js
Normal file
112
src/models/ghost/Account.js
Normal file
@ -0,0 +1,112 @@
|
||||
'use strict';
|
||||
import mongoose from 'mongoose';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
/**
|
||||
* 游戏账户
|
||||
*/
|
||||
const Account = new Schema({
|
||||
// 公众号获取的原始信息
|
||||
wechat_info: {
|
||||
wechat_id: {type: String},
|
||||
avatar: {type: String},
|
||||
nickname: {type: String},
|
||||
sex: {type: String},
|
||||
province: {type: String},
|
||||
city: {type: String},
|
||||
},
|
||||
// 金蚕游戏id
|
||||
account_id: {type: String},
|
||||
session_id: {type: String},
|
||||
// 从公众号中获取的union_id
|
||||
union_id: {type: String},
|
||||
// 对应的open_id
|
||||
open_id: {type: String},
|
||||
comment: {type: String},
|
||||
locked: {type: Boolean, default: false},
|
||||
locked_time: {type: Date},
|
||||
// 当前积分
|
||||
score: {type: Number, default: 0},
|
||||
// 账户类型,user: 普通用户
|
||||
account_type: {type: String, default: 'user'},
|
||||
// 如果account的 is_new为true, 且invite_user等于等于用户id, 则表明该用户是这个人邀请的
|
||||
is_new: {type: Boolean, default: true},
|
||||
invite_user: {type: String, ref: 'Account'},
|
||||
last_login: {type: Date},
|
||||
last_check: {type: Date},
|
||||
// 收件人信息
|
||||
address_info: {
|
||||
// 地区代码
|
||||
code: {type: String},
|
||||
// 省
|
||||
province: {type: String},
|
||||
// 市
|
||||
city: {type: String},
|
||||
// 区域
|
||||
district: {type: String},
|
||||
// 地址
|
||||
address: {type: String},
|
||||
// 收件人
|
||||
name: {type: String},
|
||||
// 联系电话
|
||||
mobile: {type: String},
|
||||
// 邮编
|
||||
postcode: {type: String},
|
||||
},
|
||||
});
|
||||
|
||||
Account.virtual('createdAt').get(function() {
|
||||
return this._id.getTimestamp();
|
||||
});
|
||||
|
||||
|
||||
Account.virtual('sex_name').get(function() {
|
||||
if (this.wechat_info.sex === '1') {
|
||||
return '男';
|
||||
} else if (this.sex === '2') {
|
||||
return '女';
|
||||
} else {
|
||||
return '未指定';
|
||||
}
|
||||
});
|
||||
|
||||
const conn = dbUtil.getConnGhost();
|
||||
const AccountModel = conn.model('Account', Account);
|
||||
|
||||
|
||||
AccountModel.getByOpenId = (openId) => {
|
||||
return AccountModel.findOne({open_id: openId});
|
||||
};
|
||||
|
||||
AccountModel.getByAccountId = (accountId) => {
|
||||
return AccountModel.findOne({account_id: accountId});
|
||||
};
|
||||
|
||||
AccountModel.updateInfo = (record, body) => {
|
||||
const wechatInfo = record.wechat_info || {};
|
||||
if (body.nickname) {
|
||||
wechatInfo.nickname = body.nickname;
|
||||
}
|
||||
if (body.avatar) {
|
||||
wechatInfo.avatar = body.avatar;
|
||||
}
|
||||
if (body.province) {
|
||||
wechatInfo.province = body.province;
|
||||
}
|
||||
if (body.city) {
|
||||
wechatInfo.city = body.city;
|
||||
}
|
||||
if (body.gender !== undefined) {
|
||||
wechatInfo.gender = body.gender;
|
||||
}
|
||||
record.wechat_info = wechatInfo;
|
||||
if (body.union_id) {
|
||||
record.union_id = body.union_id;
|
||||
}
|
||||
|
||||
return record;
|
||||
};
|
||||
|
||||
|
||||
export default AccountModel;
|
32
src/models/ghost/AppInfo.js
Normal file
32
src/models/ghost/AppInfo.js
Normal file
@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
|
||||
import mongoose from 'mongoose';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
|
||||
/**
|
||||
* 积分墙app
|
||||
* */
|
||||
const AppInfo = new Schema({
|
||||
name: {type: String},
|
||||
// 已经有多少人领取
|
||||
count: {type: Number, default: 0},
|
||||
appid: {type: String},
|
||||
// 积分
|
||||
score: {type: Number},
|
||||
desc: {type: String},
|
||||
// 图标
|
||||
icon_img: {type: String},
|
||||
sort: {type: Number},
|
||||
deleted: {type: Boolean, default: false},
|
||||
delete_time: {type: Date},
|
||||
}, {
|
||||
collection: 'app_info',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
const conn = dbUtil.getConnGhost();
|
||||
const AppInfoModel = conn.model('AppInfo', AppInfo);
|
||||
|
||||
export default AppInfoModel;
|
68
src/models/ghost/CardInfo.js
Normal file
68
src/models/ghost/CardInfo.js
Normal file
@ -0,0 +1,68 @@
|
||||
'use strict';
|
||||
|
||||
import mongoose from 'mongoose';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
|
||||
/**
|
||||
* 贺卡信息
|
||||
* */
|
||||
const CardInfo = new Schema({
|
||||
name: {type: String},
|
||||
// 已经有多少人完成纺织
|
||||
count: {type: Number, default: 0},
|
||||
count_current: {type: Number, default: 0},
|
||||
// 所需积分
|
||||
score: {type: Number},
|
||||
// 卡片故事
|
||||
desc: {type: String},
|
||||
// 图标
|
||||
icon_img: {type: String},
|
||||
// 状态, 0: 正常状态, 1: 已下架
|
||||
status: {type: Number},
|
||||
content_img: {type: String},
|
||||
images: [{type: String}],
|
||||
deleted: {type: Boolean, default: false},
|
||||
delete_time: {type: Date},
|
||||
sort_index: {type: Number},
|
||||
createdBy: {type: String},
|
||||
}, {
|
||||
collection: 'card_info',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
const conn = dbUtil.getConnGhost();
|
||||
const CardInfoModel = conn.model('CardInfo', CardInfo);
|
||||
|
||||
CardInfoModel.parse_req = (req, record) => {
|
||||
if (!record) {
|
||||
record = new CardInfoModel({
|
||||
createdBy: req.user.id,
|
||||
count: 0,
|
||||
score: 0,
|
||||
});
|
||||
}
|
||||
const body = req.body;
|
||||
record.name = body.name;
|
||||
record.desc = body.desc;
|
||||
record.score = body.score;
|
||||
record.count = body.count;
|
||||
record.images = body.images;
|
||||
record.content_img = body.content_img;
|
||||
record.icon_img = body.icon_img;
|
||||
record.status = body.status;
|
||||
record.sort_index = body.sort_index;
|
||||
return record;
|
||||
};
|
||||
|
||||
CardInfoModel.edit_validate = () => {
|
||||
return {
|
||||
form: '#card_edit_form',
|
||||
rules: [
|
||||
['name', ['required'], '<strong>卡片名</strong> 不能为空'],
|
||||
['score', ['required'], '<strong>所需积分</strong> 不能为空'],
|
||||
],
|
||||
};
|
||||
};
|
||||
export default CardInfoModel;
|
257
src/models/ghost/EmulatedGames.js
Normal file
257
src/models/ghost/EmulatedGames.js
Normal file
@ -0,0 +1,257 @@
|
||||
import mongoose from 'mongoose';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
import stringUtil from '../../utils/string.utils';
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
|
||||
const EmulatedGames = new Schema({
|
||||
gid: {type: Number, required: true},
|
||||
// 游戏名
|
||||
name: {type: String},
|
||||
/** 游戏类型
|
||||
射击 1
|
||||
格斗 2
|
||||
角色扮演 3
|
||||
动作角色扮演 4
|
||||
赛车 5
|
||||
动作游戏 6
|
||||
策略战棋 7
|
||||
其他 8
|
||||
益智游戏 9,
|
||||
体育游戏 10,
|
||||
冒险游戏 11,
|
||||
模拟战略 12,
|
||||
桌面游戏 13,
|
||||
音乐游戏 14,
|
||||
第一人称射击 15
|
||||
*/
|
||||
type: {type: Number},
|
||||
// 游戏介绍
|
||||
introduce: {type: String},
|
||||
// 游戏语言
|
||||
language: {type: String},
|
||||
// 英文名
|
||||
orgname: {type: String},
|
||||
// 图片数量
|
||||
pic_count: {type: Number},
|
||||
// 图片列表
|
||||
images: [{type: String}],
|
||||
// 标签 以半角逗号作分割
|
||||
taglist: {type: String},
|
||||
// 游戏评分
|
||||
score: {type: Number},
|
||||
// 是否是公开游戏, 公开游戏不需要购买即可玩
|
||||
open: {type: Boolean},
|
||||
// 游戏大类 fc, gba, h5, weapp, ad
|
||||
category: {type: String},
|
||||
// 该字段只对gba游戏有效, 等于2的情况下, 使用第二个模拟器的页面, 其他值使用默认模拟器
|
||||
fixed: {type: Number},
|
||||
// 排序字段, 倒序
|
||||
sortIdx: {type: Number},
|
||||
// 游戏链接, 针对h5游戏和ad等类型, 需要该字段,以供客户端跳转
|
||||
link: {type: String},
|
||||
// 游戏的二维码, 针对weapp类型的游戏, 提供该字段已扫码跳转
|
||||
qrcode: {type: String},
|
||||
// 游戏版本
|
||||
version: {type: String},
|
||||
// 游戏真实的play数量
|
||||
playCount: {type: Number, default: 0},
|
||||
// 游戏显示的play数量
|
||||
showCount: {type: Number, default: 1000},
|
||||
createdBy: {type: String},
|
||||
deleted: {type: Boolean, default: false},
|
||||
delete_time: {type: Date},
|
||||
deletedBy: {type: String},
|
||||
published: {type: Boolean, default: false},
|
||||
publish_time: {type: Date},
|
||||
publishedBy: {type: String},
|
||||
}, {
|
||||
collection: 'games',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
const gameTypes = {
|
||||
1: '射击',
|
||||
2: '格斗',
|
||||
3: '角色扮演',
|
||||
4: '动作角色扮演',
|
||||
5: '赛车',
|
||||
6: '动作游戏',
|
||||
7: '策略战棋',
|
||||
8: '其他',
|
||||
9: '益智游戏',
|
||||
10: '体育游戏',
|
||||
11: '冒险游戏',
|
||||
12: '模拟战略',
|
||||
13: '桌面游戏',
|
||||
14: '音乐游戏',
|
||||
15: '第一人称射击',
|
||||
};
|
||||
|
||||
EmulatedGames.pre('save', async function(next) {
|
||||
if (!this.gid) {
|
||||
this.gid = await EmulatedGames.nextGid(this.category);
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* 获取显示用的游戏类型
|
||||
* */
|
||||
EmulatedGames.virtual('show_type').get(function() {
|
||||
return gameTypes[this.type];
|
||||
});
|
||||
/**
|
||||
* 返回所有的游戏类型
|
||||
* */
|
||||
EmulatedGames.virtual('all_type').get(function() {
|
||||
return gameTypes;
|
||||
});
|
||||
|
||||
/**
|
||||
* 根据gid返回数据
|
||||
* @param {string} gid 游戏的短id
|
||||
* @return {Object} record
|
||||
* */
|
||||
EmulatedGames.query.byGid = function(gid) {
|
||||
return this.where({gid: gid, deleted: false});
|
||||
};
|
||||
|
||||
EmulatedGames.statics.allType = function() {
|
||||
return gameTypes;
|
||||
};
|
||||
/**
|
||||
* 获取下一个可能的gid
|
||||
* @param {string} category 游戏的类别
|
||||
* @return {number} gid 新的游戏id
|
||||
* */
|
||||
EmulatedGames.statics.nextGid = async function(category) {
|
||||
const records = await this.find({category: category}).limit(1).sort({gid: -1});
|
||||
if (records && records.length > 0) {
|
||||
return records[0].gid + 1;
|
||||
} else {
|
||||
if (category === 'gba') {
|
||||
return 1;
|
||||
} else if (category === 'fc') {
|
||||
return 7000001;
|
||||
} else if (category === 'h5') {
|
||||
return 10000001;
|
||||
} else {
|
||||
return 20000001;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EmulatedGames.statics.allLanguage = function() {
|
||||
return ['中文', '简体中文', '繁体中文', '英文', '日文', '多国语言', '德文', '法文', '欧洲', '其他']
|
||||
};
|
||||
|
||||
EmulatedGames.statics.saveWithReq = async function(req) {
|
||||
const body = req.body;
|
||||
const data = body.data;
|
||||
const ignoreKeySet = new Set(['createdAt', 'updatedAt', '__v', 'deleted', 'delete_time', 'deletedBy']);
|
||||
let record;
|
||||
if (data._id) {
|
||||
record = await this.findById(data._id);
|
||||
}
|
||||
if (!record) {
|
||||
record = new EmulatedGameModel({
|
||||
createdBy: req.user.id,
|
||||
});
|
||||
}
|
||||
for (const key in body.data) {
|
||||
if ({}.hasOwnProperty.call(body.data, key) && !ignoreKeySet.has(key)) {
|
||||
record[key] = body.data[key];
|
||||
}
|
||||
}
|
||||
return record.save();
|
||||
};
|
||||
/**
|
||||
* @param {Object} req 请求对象
|
||||
* keyStr 关键字
|
||||
* category 游戏类别, all: 所有,fc, gba
|
||||
* type 游戏类型 0: 所有, 其他值: 对应类型的游戏
|
||||
* language 游戏语言
|
||||
* tag 标签
|
||||
* open 是否公开 all: 所有, 0, 1
|
||||
* @return {Object} opt 查询条件对象
|
||||
* @return {Object} sortObj 排序对象
|
||||
* */
|
||||
EmulatedGames.statics.generateQueryOpt = function(req) {
|
||||
let opt = {};
|
||||
const body = req.body;
|
||||
const order = body.order;
|
||||
const sort = body.sort ? body.sort : 'sortIdx';
|
||||
const sortObj = {};
|
||||
sortObj[sort] = order === 'asc' ? 1 : -1;
|
||||
if (body.keyStr) {
|
||||
const orArr = [
|
||||
{name: {$regex: body.keyStr, $options: 'i'}},
|
||||
{orgname: {$regex: body.keyStr, $options: 'i'}},
|
||||
{taglist: {$regex: body.keyStr, $options: 'i'}},
|
||||
];
|
||||
opt = {$or: orArr};
|
||||
}
|
||||
if (body.category && body.category !== 'all') {
|
||||
opt.category = body.category;
|
||||
}
|
||||
if (body.type) {
|
||||
opt.type = body.type;
|
||||
}
|
||||
if (body.language) {
|
||||
opt.language = {$regex: body.language, $options: 'i'};
|
||||
}
|
||||
if (body.tag) {
|
||||
opt.taglist = {$regex: body.tag, $options: 'i'};
|
||||
}
|
||||
if (body.open && body.open !== 'all') {
|
||||
opt.open = stringUtil.isTrue(body.open);
|
||||
}
|
||||
if (body.published !== undefined && body.published !== 'all') {
|
||||
opt.published = stringUtil.isTrue(body.published);
|
||||
}
|
||||
opt.deleted = false;
|
||||
return {opt, sortObj};
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前最大的sortIdx
|
||||
* */
|
||||
EmulatedGames.statics.getMaxIndex = async function() {
|
||||
const records = await this.find({deleted: false}).limit(1).sort({sortIdx: -1});
|
||||
if (records.length > 0) {
|
||||
return records[0].sortIdx;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 获取当前数值的排名
|
||||
* @param {Number} idx 当前的sortIdx
|
||||
* @param {Number} gid 游戏的gid
|
||||
* */
|
||||
EmulatedGames.statics.currentSort = async function(idx, gid) {
|
||||
return await this.countDocuments({deleted: false, sortIdx: {$gte: idx}, gid: {$lt: gid}});
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新游戏的发布状态
|
||||
* @param {Array} ids
|
||||
* @param {Boolean} status
|
||||
* @param {String} user
|
||||
* */
|
||||
EmulatedGames.statics.updatePublishState = async function(ids, status, user) {
|
||||
if (status) {
|
||||
return await this.where({_id: {$in: ids}}).updateMany({
|
||||
published: true, publish_time: new Date, publishedBy: user});
|
||||
} else {
|
||||
return await this.where({_id: {$in: ids}}).updateMany({published: false});
|
||||
}
|
||||
};
|
||||
|
||||
const conn = dbUtil.getConnGhost();
|
||||
|
||||
const EmulatedGameModel = conn.model('EmulatedGames', EmulatedGames);
|
||||
|
||||
export default EmulatedGameModel;
|
76
src/models/ghost/ExchangeRecord.js
Normal file
76
src/models/ghost/ExchangeRecord.js
Normal file
@ -0,0 +1,76 @@
|
||||
'use strict';
|
||||
|
||||
import mongoose from 'mongoose';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
/**
|
||||
* 公仔兑换&物流记录
|
||||
* */
|
||||
const ExchangeRecord = new Schema({
|
||||
user: {type: String, ref: 'Account'},
|
||||
// 接受公仔的用户
|
||||
receive_user: {type: String, ref: 'Account'},
|
||||
// 公仔信息
|
||||
item: {type: String, ref: 'Gift'},
|
||||
// 兑换类型, 0: 自己兑换, 1: 赠送给朋友,并且填写了配送地址, 2: 赠送给朋友, 未填写配送地址
|
||||
type: {type: Number, default: 0},
|
||||
// 兑换消耗积分
|
||||
score: {type: Number},
|
||||
// 寄语
|
||||
content: {type: String},
|
||||
// 收件人
|
||||
target_name: {type: String},
|
||||
// 署名
|
||||
sign: {type: String},
|
||||
// 收件人信息, 从account中继承
|
||||
address_info: {
|
||||
// 地区代码
|
||||
code: {type: String},
|
||||
// 省
|
||||
province: {type: String},
|
||||
// 市
|
||||
city: {type: String},
|
||||
// 区域
|
||||
district: {type: String},
|
||||
// 地址
|
||||
address: {type: String},
|
||||
// 收件人
|
||||
name: {type: String},
|
||||
// 联系电话
|
||||
mobile: {type: String},
|
||||
// 邮编
|
||||
postcode: {type: String},
|
||||
},
|
||||
// 配送时间
|
||||
send_time: {type: Date},
|
||||
// 物流公司名称
|
||||
company: {type: String},
|
||||
// 快递单号
|
||||
invoice: {type: String},
|
||||
// 状态 0: 未领取, 1: 已领取, -1: 已退回, 2: 已配送
|
||||
status: {type: Number, default: 0},
|
||||
}, {
|
||||
collection: 'exchange_record',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
ExchangeRecord.virtual('statusStr').get(function() {
|
||||
switch (this.status) {
|
||||
case -1:
|
||||
return '已退回';
|
||||
case 1:
|
||||
return '未发货';
|
||||
case 0:
|
||||
return '未领取';
|
||||
case 2:
|
||||
return '已发货';
|
||||
case -2:
|
||||
return '服务端退回';
|
||||
}
|
||||
});
|
||||
|
||||
const conn = dbUtil.getConnGhost();
|
||||
const ExchangeRecordModel = conn.model('ExchangeRecord', ExchangeRecord);
|
||||
|
||||
export default ExchangeRecordModel;
|
87
src/models/ghost/Gift.js
Normal file
87
src/models/ghost/Gift.js
Normal file
@ -0,0 +1,87 @@
|
||||
'use strict';
|
||||
|
||||
import mongoose from 'mongoose';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
|
||||
/**
|
||||
* 公仔信息
|
||||
* */
|
||||
const Gift = new Schema({
|
||||
// 公仔名
|
||||
name: {type: String},
|
||||
// 详细说明
|
||||
desc: {type: String},
|
||||
// 大小
|
||||
size: {type: String},
|
||||
// 总数
|
||||
amount: {type: Number},
|
||||
// 已兑换数量
|
||||
amount_used: {type: Number},
|
||||
// 当前正在编织数量
|
||||
amount_current: {type: Number, default: 0},
|
||||
// 所需积分
|
||||
score: {type: Number},
|
||||
// 实物图, 大图
|
||||
main_img: {type: String},
|
||||
// 大图
|
||||
content_img: {type: String},
|
||||
// 介绍图片列表
|
||||
images: [{type: String}],
|
||||
// 小图
|
||||
icon_img: {type: String},
|
||||
areas: [{type: String}],
|
||||
// 状态, 0: 正常状态, 1: 已下架, 2: 往期商品
|
||||
status: {type: Number},
|
||||
deleted: {type: Boolean, default: false},
|
||||
delete_time: {type: Date},
|
||||
// 序号
|
||||
sort_index: {type: Number},
|
||||
createdBy: {type: String},
|
||||
}, {
|
||||
collection: 'gifts',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
const conn = dbUtil.getConnGhost();
|
||||
const GiftModel = conn.model('Gift', Gift);
|
||||
|
||||
GiftModel.parse_gift = (req, record) => {
|
||||
if (!record) {
|
||||
record = new GiftModel({
|
||||
createdBy: req.user.id,
|
||||
amount: 0,
|
||||
amount_used: 0,
|
||||
score: 0,
|
||||
});
|
||||
}
|
||||
const body = req.body;
|
||||
record.name = body.name;
|
||||
record.desc = body.desc;
|
||||
record.size = body.size;
|
||||
record.amount = body.amount;
|
||||
record.amount_used = body.amount_used;
|
||||
record.score = body.score;
|
||||
record.main_img = body.main_img;
|
||||
record.content_img = body.content_img;
|
||||
record.images = body.images;
|
||||
record.areas = body.areas;
|
||||
record.icon_img = body.icon_img;
|
||||
record.status = body.status;
|
||||
record.sort_index = body.sort_index;
|
||||
return record;
|
||||
};
|
||||
|
||||
GiftModel.gift_edit_validate = () => {
|
||||
return {
|
||||
form: '#gift_edit_form',
|
||||
rules: [
|
||||
['name', ['required'], '<strong>公仔名</strong> 不能为空'],
|
||||
['score', ['required'], '<strong>所需积分</strong> 不能为空'],
|
||||
['amount', ['required'], '<strong>库存总数</strong> 不能为空'],
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
export default GiftModel;
|
34
src/models/ghost/Message.js
Normal file
34
src/models/ghost/Message.js
Normal file
@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
import mongoose from 'mongoose';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
|
||||
/**
|
||||
* 消息记录
|
||||
* */
|
||||
const Message = new Schema({
|
||||
// 帐号信息
|
||||
user: {type: String, ref: 'Account'},
|
||||
msg: {type: String},
|
||||
// 消息状态, 0: 未下发, 1: 已下发
|
||||
status: {type: Number},
|
||||
}, {
|
||||
collection: 'messages',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
const conn = dbUtil.getConnGhost();
|
||||
const MessageModel = conn.model('Message', Message);
|
||||
|
||||
MessageModel.addOneRecord = function(uid, msg) {
|
||||
const record = new MessageModel({
|
||||
user: uid,
|
||||
msg: msg,
|
||||
status: 0,
|
||||
});
|
||||
return record.save();
|
||||
};
|
||||
|
||||
export default MessageModel;
|
62
src/models/ghost/RecommendGames.js
Normal file
62
src/models/ghost/RecommendGames.js
Normal file
@ -0,0 +1,62 @@
|
||||
|
||||
import mongoose from 'mongoose';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
/**
|
||||
* 推荐游戏
|
||||
* */
|
||||
const RecommendGamesSchema = new Schema({
|
||||
// 游戏id
|
||||
gid: {type: String, ref: 'Games'},
|
||||
// 0: 游戏, 1: 链接
|
||||
type: {type: Number, default: 0},
|
||||
link: {type: String},
|
||||
image: {type: String},
|
||||
name: {type: String},
|
||||
sortIdx: {type: Number, default: 0},
|
||||
createdBy: {type: String},
|
||||
deleted: {type: Boolean, default: false},
|
||||
delete_time: {type: Date},
|
||||
deletedBy: {type: String},
|
||||
}, {
|
||||
collection: 'recommend_games',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
* */
|
||||
class RecommendGamesClass {
|
||||
/**
|
||||
* 分析request, 保存对记录的更改
|
||||
* @param {Object} req
|
||||
* @return {Promise} record
|
||||
* */
|
||||
static async saveWithReq(req) {
|
||||
const body = req.body;
|
||||
const data = body.data;
|
||||
const ignoreKeySet = new Set(['createdAt', 'updatedAt', '__v', 'deleted', 'delete_time', 'deletedBy']);
|
||||
let record;
|
||||
if (data._id) {
|
||||
record = await this.findById(data._id);
|
||||
}
|
||||
if (!record) {
|
||||
record = new RecommendGamesModel({
|
||||
createdBy: req.user.id,
|
||||
});
|
||||
}
|
||||
for (const key in body.data) {
|
||||
if ({}.hasOwnProperty.call(body.data, key) && !ignoreKeySet.has(key)) {
|
||||
record[key] = body.data[key];
|
||||
}
|
||||
}
|
||||
return record.save();
|
||||
}
|
||||
}
|
||||
RecommendGamesSchema.loadClass(RecommendGamesClass);
|
||||
|
||||
const conn = dbUtil.getConnGhost();
|
||||
const RecommendGamesModel = conn.model('RecommendGames', RecommendGamesSchema);
|
||||
|
||||
export default RecommendGamesModel;
|
50
src/models/ghost/ScoreRecord.js
Normal file
50
src/models/ghost/ScoreRecord.js
Normal file
@ -0,0 +1,50 @@
|
||||
'use strict';
|
||||
|
||||
import mongoose from 'mongoose';
|
||||
import moment from 'moment';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
/**
|
||||
* 积分变动记录
|
||||
* */
|
||||
const ScoreRecord = new Schema({
|
||||
// 关联的帐号
|
||||
user: {type: String, ref: 'Account'},
|
||||
// 关联的幽灵
|
||||
ghost: {type: String, ref: 'Ghost'},
|
||||
// 关联的兑换记录
|
||||
exchange_record: {type: String, ref: 'ExchangeRecord'},
|
||||
// 卡片兑换记录
|
||||
card_record: {type: String, ref: 'CardRecord'},
|
||||
appid: {type: String},
|
||||
// 日期, YYYY-MM-DD
|
||||
day: {type: String},
|
||||
// 类型:
|
||||
// ghost: 幽灵产生, gift: 看广告加速, ghost_init: 新幽灵初始增加, click_app: 点击积分墙,
|
||||
// exchange: 兑换, card: 兑换卡片, money: 兑换现金, retract: 公仔退回, retract_svr: 系统退回
|
||||
type: {type: String},
|
||||
// 操作的管理员帐号
|
||||
account_admin: {type: String},
|
||||
// 变动数量
|
||||
amount: {type: Number},
|
||||
}, {
|
||||
collection: 'score_record',
|
||||
timestamps: true,
|
||||
});
|
||||
const conn = dbUtil.getConnGhost();
|
||||
const ScoreRecordModel = conn.model('ScoreRecord', ScoreRecord);
|
||||
|
||||
// 添加一条公仔退回记录
|
||||
ScoreRecordModel.addRetractScore = function(uid, amount, eid, adminId) {
|
||||
const record = new ScoreRecordModel({
|
||||
user: uid,
|
||||
amount: amount,
|
||||
exchange_record: eid,
|
||||
account_admin: adminId,
|
||||
type: 'retract_svr',
|
||||
day: moment().format('YYYY-MM-DD'),
|
||||
});
|
||||
return record.save();
|
||||
};
|
||||
export default ScoreRecordModel;
|
23
src/models/snoopy/ChinaArea.js
Normal file
23
src/models/snoopy/ChinaArea.js
Normal file
@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
import mongoose from 'mongoose';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
|
||||
/**
|
||||
* 中国区域定义
|
||||
*/
|
||||
const ChinaArea = new mongoose.Schema({
|
||||
// 区域名
|
||||
name: {type: String},
|
||||
// 包含地区
|
||||
locations: [{type: String}],
|
||||
deleted: {type: Boolean, default: false},
|
||||
delete_time: {type: Date},
|
||||
}, {
|
||||
collection: 'china_area',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
const conn = dbUtil.getConnSnoopy();
|
||||
|
||||
export default conn.model('ChinaArea', ChinaArea);
|
||||
|
45
src/models/snoopy/ChinaRegion.js
Normal file
45
src/models/snoopy/ChinaRegion.js
Normal file
@ -0,0 +1,45 @@
|
||||
'use strict';
|
||||
import mongoose from 'mongoose';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
|
||||
/**
|
||||
* 中国行政区划
|
||||
*/
|
||||
const ChinaRegion = new mongoose.Schema({
|
||||
// 区划id, 6位数字
|
||||
_id: {type: Number},
|
||||
// 等级, 0: 国, 1: 省, 2: 市, 3: 区县
|
||||
level: {type: Number},
|
||||
// 父级id
|
||||
parent_id: {type: Number},
|
||||
// 中文名
|
||||
name: {type: String},
|
||||
// 中文名, 短
|
||||
short_name: {type: String},
|
||||
// 拼音
|
||||
pinyin: {type: String, index: true},
|
||||
// 国家
|
||||
country: {type: String},
|
||||
// 省份, 冗余数据, 方便查找, 全中国总共才2000多条数据, 没什么问题
|
||||
province: {type: String},
|
||||
// 市
|
||||
city: {type: String},
|
||||
// 区县
|
||||
district: {type: String},
|
||||
// 邮编
|
||||
zipcode: {type: String},
|
||||
// 区号
|
||||
citycode: {type: String},
|
||||
// 经度
|
||||
lan: {type: Number},
|
||||
// 维度
|
||||
lat: {type: Number},
|
||||
}, {
|
||||
collection: 'china_region',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
const conn = dbUtil.getConnSnoopy();
|
||||
|
||||
export default conn.model('ChinaRegion', ChinaRegion);
|
||||
|
49
src/models/snoopy/CustomerReplay.js
Normal file
49
src/models/snoopy/CustomerReplay.js
Normal file
@ -0,0 +1,49 @@
|
||||
'use strict';
|
||||
import mongoose from 'mongoose';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
|
||||
/**
|
||||
* 客服关键字回复
|
||||
*/
|
||||
const CustomerReplay = new mongoose.Schema({
|
||||
// 游戏id
|
||||
game_id: {type: String},
|
||||
// 响应的关键字
|
||||
keys: [{type: String}],
|
||||
items: [{
|
||||
_id: false,
|
||||
item_id: {type: String},
|
||||
count: {type: Number},
|
||||
}],
|
||||
actived: {type: Boolean, default: true},
|
||||
// 备注
|
||||
comment: {type: String},
|
||||
createdBy: {type: String},
|
||||
deleted: {type: Boolean, default: false},
|
||||
deletedBy: {type: String},
|
||||
delete_time: {type: Date},
|
||||
}, {
|
||||
collection: 'customer_replay',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
const conn = dbUtil.getConnSnoopy();
|
||||
const CustomerReplayModel = conn.model('CustomerReplay', CustomerReplay);
|
||||
|
||||
CustomerReplayModel.parseReq = function(req, record) {
|
||||
if (!record) {
|
||||
record = new CustomerReplayModel({
|
||||
createdBy: req.user.id,
|
||||
});
|
||||
}
|
||||
const body = req.body;
|
||||
record.game_id = body.game_id;
|
||||
record.keys = body.keys;
|
||||
record.items = body.items;
|
||||
record.actived = body.actived;
|
||||
record.comment = body.comment;
|
||||
return record;
|
||||
};
|
||||
|
||||
export default CustomerReplayModel;
|
||||
|
116
src/models/snoopy/GameInfo.js
Normal file
116
src/models/snoopy/GameInfo.js
Normal file
@ -0,0 +1,116 @@
|
||||
'use strict';
|
||||
import mongoose from 'mongoose';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
|
||||
/**
|
||||
* 游戏信息
|
||||
*/
|
||||
const GameInfo = new mongoose.Schema({
|
||||
// 游戏id
|
||||
game_id: {type: String},
|
||||
// 游戏名
|
||||
game_name: {type: String},
|
||||
// 英文名
|
||||
game_name_en: {type: String},
|
||||
// 游戏icon
|
||||
game_icon: {type: String},
|
||||
// 游戏类型
|
||||
game_type: {type: Number},
|
||||
// app id
|
||||
app_id: {type: String},
|
||||
// app secret
|
||||
app_secret: {type: String},
|
||||
// 状态
|
||||
status: {type: Number, default: 0},
|
||||
// 平台
|
||||
platform: {type: String},
|
||||
// 关联的游戏列表
|
||||
linked_games: [{type: String, ref: 'GameInfo'}],
|
||||
// 排序
|
||||
sort: {type: Number, index: true},
|
||||
// 是否主推
|
||||
is_recommend: {type: Boolean, default: false},
|
||||
// 是否火爆
|
||||
is_hot: {type: Boolean, default: false},
|
||||
// 是否是新游
|
||||
is_new: {type: Boolean, default: false},
|
||||
// 备注
|
||||
comment: {type: String},
|
||||
// 点击率
|
||||
click_count: {type: Number, default: 0},
|
||||
deleted: {type: Boolean, default: false},
|
||||
deletedBy: {type: String},
|
||||
delete_time: {type: Date},
|
||||
}, {
|
||||
collection: 'game_info',
|
||||
timestamps: true,
|
||||
});
|
||||
const showTypes = {
|
||||
0: '益智解密',
|
||||
1: '手脑反应',
|
||||
2: '策略养成',
|
||||
};
|
||||
|
||||
const platforms = {
|
||||
6000: '内部测试',
|
||||
6001: '微信',
|
||||
6002: 'QQ玩一玩',
|
||||
6003: 'OPPO小游戏',
|
||||
6004: 'VIVO快游戏',
|
||||
6501: 'facebook小游戏',
|
||||
6005: '百度',
|
||||
6006: '抖音',
|
||||
};
|
||||
|
||||
const statusObj = {
|
||||
0: '已上线',
|
||||
1: '暂停',
|
||||
2: '新版本开发中',
|
||||
3: '外包开发中',
|
||||
4: '开发中',
|
||||
5: '未开始',
|
||||
6: '测试中',
|
||||
7: '二次开发中',
|
||||
};
|
||||
|
||||
GameInfo.virtual('show_type').get(function() {
|
||||
return showTypes[this.game_type];
|
||||
});
|
||||
|
||||
GameInfo.virtual('all_type').get(function() {
|
||||
return showTypes;
|
||||
});
|
||||
|
||||
|
||||
GameInfo.virtual('platform_show').get(function() {
|
||||
return platforms[this.platform];
|
||||
});
|
||||
|
||||
GameInfo.virtual('status_show').get(function() {
|
||||
return statusObj[this.status];
|
||||
});
|
||||
|
||||
GameInfo.statics = {
|
||||
all_type() {
|
||||
return showTypes;
|
||||
},
|
||||
all_platform() {
|
||||
return platforms;
|
||||
},
|
||||
status_list() {
|
||||
const list = [];
|
||||
for (const key in statusObj) {
|
||||
if ({}.hasOwnProperty.call(statusObj, key)) {
|
||||
list.push({
|
||||
key: key,
|
||||
name: statusObj[key],
|
||||
});
|
||||
}
|
||||
}
|
||||
return list;
|
||||
},
|
||||
};
|
||||
const conn = dbUtil.getConnSnoopy();
|
||||
|
||||
export default conn.model('GameInfo', GameInfo);
|
||||
|
97
src/models/snoopy/GameShareImage.js
Normal file
97
src/models/snoopy/GameShareImage.js
Normal file
@ -0,0 +1,97 @@
|
||||
'use strict';
|
||||
import mongoose from 'mongoose';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
import stringUtil from '../../utils/string.utils';
|
||||
/**
|
||||
* 游戏分享图
|
||||
*/
|
||||
const GameShareImage = new mongoose.Schema({
|
||||
// 游戏id
|
||||
game_id: {type: String},
|
||||
// 是否是默认分享
|
||||
default_share: {type: Boolean, default: false},
|
||||
// 分享类型
|
||||
share_type: {type: String},
|
||||
// 分享图路径
|
||||
share_image: {type: String},
|
||||
// 分享语
|
||||
share_word: {type: String},
|
||||
// 多个分享图
|
||||
share_images: [{type: String}],
|
||||
// 多个分享语
|
||||
share_words: [{type: String}],
|
||||
// 区域
|
||||
area: {type: String},
|
||||
// 地域列表
|
||||
locations: [{type: String}],
|
||||
// 性别
|
||||
sex: {type: String},
|
||||
// 广告id
|
||||
ad_id: {type: String},
|
||||
// 分享次数
|
||||
share_count: {type: Number, default: 0},
|
||||
// 广告次数
|
||||
ad_count: {type: Number, default: 0},
|
||||
// 广告间隔时间
|
||||
ad_cd: {type: Number, default: 0},
|
||||
// 0: 分享图, 1: 广告
|
||||
type: {type: Number, default: 0},
|
||||
// 备注
|
||||
comment: {type: String},
|
||||
createdBy: {type: String},
|
||||
deleted: {type: Boolean, default: false},
|
||||
deletedBy: {type: String},
|
||||
delete_time: {type: Date},
|
||||
}, {
|
||||
collection: 'game_share_images',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
const conn = dbUtil.getConnSnoopy();
|
||||
const GameShareImageModel = conn.model('GameShareImage', GameShareImage);
|
||||
|
||||
GameShareImageModel.parse_req = (req, record) => {
|
||||
if (!record) {
|
||||
record = new GameShareImageModel({
|
||||
createdBy: req.user.id,
|
||||
});
|
||||
}
|
||||
const body = req.body;
|
||||
record.game_id = body.game_id;
|
||||
record.default_share = stringUtil.isTrue(body.default_share);
|
||||
record.share_type = body.share_type;
|
||||
record.share_images = body.share_images;
|
||||
record.share_words = body.share_words;
|
||||
if (record.share_images && record.share_images.length > 0) {
|
||||
record.share_image = record.share_images[0];
|
||||
}
|
||||
if (record.share_words && record.share_words.length > 0) {
|
||||
record.share_word = record.share_words[0];
|
||||
}
|
||||
record.locations = body.locations;
|
||||
record.sex = body.sex;
|
||||
record.area = body.area;
|
||||
record.comment = body.comment;
|
||||
record.type = body.type;
|
||||
record.ad_id = body.ad_id;
|
||||
record.ad_count = body.ad_count;
|
||||
record.ad_cd = body.ad_cd;
|
||||
record.share_count = body.share_count;
|
||||
return record;
|
||||
};
|
||||
|
||||
GameShareImageModel.edit_validate = () => {
|
||||
return {
|
||||
form: '#edit_form',
|
||||
rules: [
|
||||
['share_type', ['required'], '请选择<strong>分享类型</strong> '],
|
||||
['share_count', ['required'], '<strong>分享次数</strong> 不能为空'],
|
||||
['ad_count', ['required'], '<strong>广告播放次数</strong> 不能为空'],
|
||||
['ad_cd', ['required'], '<strong>广告播放间隔</strong> 不能为空'],
|
||||
['type', ['required'], '请选择<strong>优先级</strong>'],
|
||||
],
|
||||
};
|
||||
};
|
||||
export default GameShareImageModel;
|
||||
|
||||
|
96
src/models/snoopy/Gift.js
Normal file
96
src/models/snoopy/Gift.js
Normal file
@ -0,0 +1,96 @@
|
||||
import mongoose from 'mongoose';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
import moment from 'moment';
|
||||
|
||||
const GiftSchema = new mongoose.Schema({
|
||||
items: [{
|
||||
_id: false,
|
||||
itemid: {type: String},
|
||||
itemnum: {type: Number, default: 0},
|
||||
itemname: {type: String},
|
||||
}],
|
||||
// 礼包名
|
||||
name: {type: String},
|
||||
game_id: {type: String},
|
||||
// 状态
|
||||
status: {type: Number, default: 0},
|
||||
// 备注
|
||||
comment: {type: String},
|
||||
createdBy: {type: String},
|
||||
deleted: {type: Boolean, default: false},
|
||||
deletedBy: {type: String},
|
||||
delete_time: {type: Date},
|
||||
}, {
|
||||
collection: 'gift',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
* */
|
||||
class GiftClass {
|
||||
/**
|
||||
* 分析request, 保存对记录的更改
|
||||
* @param {Object} req
|
||||
* @param {Object} record
|
||||
* @return {Promise} record
|
||||
* */
|
||||
static parseReq(req, record) {
|
||||
const body = req.body;
|
||||
if (!record) {
|
||||
record = new GiftModel({
|
||||
createdBy: req.user.id,
|
||||
});
|
||||
}
|
||||
record.game_id = body.game_id;
|
||||
record.name = body.name;
|
||||
record.items = body.items;
|
||||
record.status = body.status;
|
||||
record.comment = body.comment;
|
||||
return record.save();
|
||||
}
|
||||
/**
|
||||
* 分析request, 并生成查询的条件和排序Object
|
||||
* @param {Object} req
|
||||
* @return {Object} opt
|
||||
* @return {Object} sortObj
|
||||
* */
|
||||
static parseGiftQueryOpt(req) {
|
||||
let opt = {};
|
||||
const body = req.body;
|
||||
const keyStr = body.name;
|
||||
let timeBegin = body.timeBegin;
|
||||
let timeEnd = body.timeEnd;
|
||||
const order = body.order;
|
||||
const sort = body.sort ? body.sort : 'createdAt';
|
||||
const sortObj = {sort: order === 'asc' ? 1 : -1};
|
||||
sortObj[sort] = order === 'asc' ? 1 : -1;
|
||||
if (keyStr) {
|
||||
opt = {$or: [
|
||||
{name: {$regex: keyStr, $options: 'i'}},
|
||||
{comment: {$regex: keyStr, $options: 'i'}},
|
||||
{'items.itemname': {$regex: keyStr, $options: 'i'}},
|
||||
]};
|
||||
}
|
||||
(body.gameId) && (opt.game_id = body.gameId);
|
||||
if (timeBegin && !timeEnd) {
|
||||
timeBegin = moment(timeBegin, 'YYYY-MM-DD').startOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||
opt.createdAt = {$gte: timeBegin};
|
||||
} else if (timeBegin && timeEnd) {
|
||||
timeBegin = moment(timeBegin, 'YYYY-MM-DD').startOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||
timeEnd = moment(timeEnd, 'YYYY-MM-DD').endOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||
opt['$and'] = [{createdAt: {$gte: timeBegin}}, {createdAt: {$lte: timeEnd}}];
|
||||
} else if (!timeBegin && timeEnd) {
|
||||
timeEnd = moment(timeEnd, 'YYYY-MM-DD').endOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||
opt.createdAt = {$lte: timeEnd};
|
||||
}
|
||||
opt.deleted = false;
|
||||
return {opt, sortObj};
|
||||
}
|
||||
}
|
||||
|
||||
GiftSchema.loadClass(GiftClass);
|
||||
|
||||
const GiftModel = dbUtil.getConnSnoopy().model('Gift', GiftSchema);
|
||||
|
||||
export default GiftModel;
|
27
src/models/snoopy/GiftHistory.js
Normal file
27
src/models/snoopy/GiftHistory.js
Normal file
@ -0,0 +1,27 @@
|
||||
import mongoose from 'mongoose';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
|
||||
/**
|
||||
* 兑换码兑换记录
|
||||
*/
|
||||
const GiftHistory = new mongoose.Schema({
|
||||
// 兑换码
|
||||
gift_no: {type: String},
|
||||
gift: {type: String, ref: 'Gift'},
|
||||
gift_pack: {type: String, ref: 'GiftPack'},
|
||||
account_id: {type: String},
|
||||
// 备注
|
||||
comment: {type: String},
|
||||
deleted: {type: Boolean, default: false},
|
||||
deletedBy: {type: String},
|
||||
delete_time: {type: Date},
|
||||
}, {
|
||||
collection: 'gift_history',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
const conn = dbUtil.getConnSnoopy();
|
||||
const GiftHistoryModel = conn.model('GiftHistory', GiftHistory);
|
||||
|
||||
export default GiftHistoryModel;
|
||||
|
191
src/models/snoopy/GiftPack.js
Normal file
191
src/models/snoopy/GiftPack.js
Normal file
@ -0,0 +1,191 @@
|
||||
import mongoose from 'mongoose';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
import stringUtil from '../../utils/string.utils';
|
||||
import moment from 'moment';
|
||||
import Games from './GameInfo';
|
||||
import logger from '../../utils/logger';
|
||||
import Gift from './Gift';
|
||||
|
||||
/**
|
||||
* 礼包
|
||||
*/
|
||||
const GiftPack = new mongoose.Schema({
|
||||
// 兑换码
|
||||
gift_nos: [{type: String}],
|
||||
gift_no_count: {type: Number},
|
||||
// 游戏id
|
||||
game_id: {type: String},
|
||||
// 服务器
|
||||
svr_id: {type: String},
|
||||
svr_name: {type: String},
|
||||
// 活动名
|
||||
name: {type: String},
|
||||
gift: {type: String, ref: 'Gift'},
|
||||
// 批次号
|
||||
batch_no: {type: Number, index: true},
|
||||
// 开始时间
|
||||
begin_time: {type: Date},
|
||||
// 结束时间
|
||||
end_time: {type: Date},
|
||||
// 渠道
|
||||
platforms: [{type: String}],
|
||||
// 类型, one: 一次性(只能一人兑换一次), batch: 批量(可供多人兑换,每人一次)
|
||||
type: {type: String},
|
||||
// 状态
|
||||
status: {type: Number, default: 0},
|
||||
// 兑换需要的vip等级
|
||||
vip: {type: Number, default: 0},
|
||||
// 备注
|
||||
comment: {type: String},
|
||||
createdBy: {type: String},
|
||||
deleted: {type: Boolean, default: false},
|
||||
deletedBy: {type: String},
|
||||
delete_time: {type: Date},
|
||||
}, {
|
||||
collection: 'gift_pack',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
GiftPack.pre('save', async function(next) {
|
||||
if (!this.batch_no) {
|
||||
let batchNo = await GiftPackModel.nextBatchNo();
|
||||
this.batch_no = batchNo;
|
||||
if (this.gift_no_count > 0) {
|
||||
this.gift_nos = GiftPackModel.generateGiftNos(this.gift_no_count, batchNo);
|
||||
}
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
const conn = dbUtil.getConnSnoopy();
|
||||
const GiftPackModel = conn.model('GiftPack', GiftPack);
|
||||
|
||||
// 生成指定数量的兑换码
|
||||
GiftPackModel.generateGiftNos = function(count, batchNo) {
|
||||
const set = new Set();
|
||||
for (let i = 0; i < count; i++) {
|
||||
const str = GiftPackModel.randomGiftNos(set);
|
||||
set.add(str);
|
||||
}
|
||||
return [...set].map((o)=> batchNo + o);
|
||||
};
|
||||
|
||||
// 生成一条随机的兑换码
|
||||
GiftPackModel.randomGiftNos = function(set) {
|
||||
const num = stringUtil.randomNumAdv(0, 2176782335);
|
||||
const str = stringUtil.string10to36(num).padStart(6, '0');
|
||||
if (set.has(str)) {
|
||||
return GiftPackModel.randomGiftNos(set);
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
GiftPackModel.nextBatchNo = async () =>{
|
||||
const record = await GiftPackModel.find({deleted: false}).limit(1).sort({batch_no: -1});
|
||||
if (record.length > 0) {
|
||||
return parseInt(record[0].batch_no) + 1;
|
||||
} else {
|
||||
return 1001;
|
||||
}
|
||||
};
|
||||
GiftPackModel.parseReq = async (req, record) => {
|
||||
const body = req.body;
|
||||
if (!record) {
|
||||
record = new GiftPackModel({
|
||||
createdBy: req.user.id,
|
||||
});
|
||||
record.gift_no_count = body.gift_count;
|
||||
}
|
||||
record.name = body.name;
|
||||
record.game_id = body.game_id;
|
||||
record.gift = body.gift;
|
||||
if (body.begin_time) {
|
||||
record.begin_time = moment(body.begin_time, 'YYYY-MM-DD HH:mm').toDate();
|
||||
} else {
|
||||
record.begin_time = null;
|
||||
}
|
||||
if (body.end_time) {
|
||||
record.end_time = moment(body.end_time, 'YYYY-MM-DD HH:mm').toDate();
|
||||
} else {
|
||||
record.end_time = null;
|
||||
}
|
||||
record.vip = body.vip;
|
||||
record.svr_id = body.svr_id;
|
||||
record.svr_name = body.svr_name;
|
||||
record.platforms = body.platforms;
|
||||
record.type = body.type;
|
||||
record.status = body.status;
|
||||
return record.save();
|
||||
};
|
||||
|
||||
GiftPackModel.parseQueryOpt = async (req) => {
|
||||
let opt = {};
|
||||
const query = req.query;
|
||||
const keyStr = query.keyStr;
|
||||
const status = parseInt(query.status);
|
||||
const type = query.type;
|
||||
const platform = query.platform;
|
||||
let timeBegin = query.timeBegin;
|
||||
let timeEnd = query.timeEnd;
|
||||
const order = query.order;
|
||||
const gameId = query.gameId;
|
||||
const sort = query.sort ? query.sort : 'createdAt';
|
||||
const sortObj = {};
|
||||
const vip = query.vip;
|
||||
sortObj[sort] = order === 'asc' ? 1 : -1;
|
||||
if (keyStr) {
|
||||
const orArr = [{name: {$regex: keyStr, $options: 'i'}}];
|
||||
try {
|
||||
const gamesArr = [
|
||||
{game_name: {$regex: keyStr, $options: 'i'}},
|
||||
{game_name_en: {$regex: keyStr, $options: 'i'}},
|
||||
];
|
||||
const games = await Games.find({$or: gamesArr});
|
||||
const gameIds = games.map((o) => o.id);
|
||||
if (gameIds.length > 0) orArr.push({game_id: {$in: gameIds}});
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
}
|
||||
try {
|
||||
const items = await Gift.find({name: {$regex: keyStr, $options: 'i'}});
|
||||
const itemIds = items.map((o) => o.id);
|
||||
if (itemIds.length > 0) orArr.push({gift: {$in: itemIds}});
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
}
|
||||
|
||||
opt = {$or: orArr};
|
||||
}
|
||||
if (status > -999) {
|
||||
opt.status = status;
|
||||
}
|
||||
if (type !== 'all') {
|
||||
opt.type = type;
|
||||
}
|
||||
if (platform !== 'all') {
|
||||
opt.platforms = {$in: [platform]};
|
||||
}
|
||||
if (vip > -1) {
|
||||
// opt.vip = {$lte: vip};
|
||||
opt.vip = vip;
|
||||
}
|
||||
if (gameId) {
|
||||
opt.game_id = gameId;
|
||||
}
|
||||
if (timeBegin && !timeEnd) {
|
||||
timeBegin = moment(timeBegin, 'YYYY-MM-DD').startOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||
opt.end_time = {$gte: timeBegin};
|
||||
} else if (timeBegin && timeEnd) {
|
||||
timeBegin = moment(timeBegin, 'YYYY-MM-DD').startOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||
timeEnd = moment(timeBegin, 'YYYY-MM-DD').endOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||
opt['$or'] = [{end_time: {$gte: timeBegin}}, {begin_time: {$lte: timeEnd}}];
|
||||
} else if (!timeBegin && timeEnd) {
|
||||
timeEnd = moment(timeBegin, 'YYYY-MM-DD').endOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||
opt.begin_time = {$lte: timeEnd};
|
||||
}
|
||||
opt.deleted = false;
|
||||
return {opt, sortObj};
|
||||
};
|
||||
|
||||
export default GiftPackModel;
|
||||
|
58
src/models/snoopy/Puzzle.js
Normal file
58
src/models/snoopy/Puzzle.js
Normal file
@ -0,0 +1,58 @@
|
||||
'use strict';
|
||||
import mongoose from 'mongoose';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
|
||||
/**
|
||||
* 竞猜题库
|
||||
*/
|
||||
|
||||
const Puzzle = new mongoose.Schema({
|
||||
// 类型, 1: 图片, 2: 音频, 3: 视频, 0: 纯文本
|
||||
attachtype: {type: Number},
|
||||
// 图片或者音频的url地址
|
||||
attach: {type: String},
|
||||
// 问题
|
||||
question: {type: String},
|
||||
// 答案扩展字符
|
||||
option: {type: String},
|
||||
// 答案
|
||||
answer: {type: String},
|
||||
// 扩展信息, 在猜菜名中, 该字段为菜系
|
||||
extra: {type: String},
|
||||
// 提示信息
|
||||
tip: {type: String},
|
||||
// 题目分类, food: 菜
|
||||
type: {type: String},
|
||||
// 备注
|
||||
comment: {type: String},
|
||||
createdBy: {type: String},
|
||||
deleted: {type: Boolean, default: false},
|
||||
deletedBy: {type: String},
|
||||
delete_time: {type: Date},
|
||||
}, {
|
||||
collection: 'puzzle',
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
const conn = dbUtil.getConnSnoopy();
|
||||
const PuzzleModel = conn.model('Puzzle', Puzzle);
|
||||
|
||||
PuzzleModel.parseReq = (req, record) => {
|
||||
if (!record) {
|
||||
record = new PuzzleModel({
|
||||
createdBy: req.user.id,
|
||||
});
|
||||
}
|
||||
const body = req.body;
|
||||
record.attachtype = body.attachtype;
|
||||
record.attach = body.attach;
|
||||
record.option = body.option;
|
||||
record.question = body.question;
|
||||
record.answer = body.answer;
|
||||
record.extra = body.extra;
|
||||
record.tip = body.tip;
|
||||
record.type = body.type;
|
||||
return record.save();
|
||||
};
|
||||
export default PuzzleModel;
|
||||
|
82
src/models/snoopy/PuzzleLevel.js
Normal file
82
src/models/snoopy/PuzzleLevel.js
Normal file
@ -0,0 +1,82 @@
|
||||
'use strict';
|
||||
import mongoose from 'mongoose';
|
||||
import dbUtil from '../../utils/db.util';
|
||||
import Puzzle from './Puzzle';
|
||||
|
||||
/**
|
||||
* 竞猜游戏关卡
|
||||
*/
|
||||
|
||||
const PuzzleLevel = new mongoose.Schema({
|
||||
game_id: {type: String, ref: 'GameInfo'},
|
||||
levels: [
|
||||
{
|
||||
_id: false,
|
||||
name: {type: String},
|
||||
index: {type: Number},
|
||||
sub_levels: [
|
||||
{
|
||||
_id: false,
|
||||
name: {type: String},
|
||||
index: {type: Number},
|
||||
subjects: [{type: String, ref: 'Puzzle'}],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
cdn_base: {type: String},
|
||||
createdBy: {type: String},
|
||||
deleted: {type: Boolean, default: false},
|
||||
deletedBy: {type: String},
|
||||
delete_time: {type: Date},
|
||||
}, {
|
||||
collection: 'puzzle_level',
|
||||
usePushEach: true,
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
const conn = dbUtil.getConnSnoopy();
|
||||
const PuzzleLevelModel = conn.model('PuzzleLevel', PuzzleLevel);
|
||||
|
||||
PuzzleLevelModel.parseReq = (req, record) => {
|
||||
if (!record) {
|
||||
record = new PuzzleLevelModel({
|
||||
createdBy: req.user.id,
|
||||
});
|
||||
}
|
||||
return record.save();
|
||||
};
|
||||
|
||||
PuzzleLevelModel.findByGame = (gameId) => {
|
||||
return PuzzleLevelModel.findOne({game_id: gameId, deleted: false});
|
||||
};
|
||||
|
||||
PuzzleLevelModel.generateLevelObj = (level) => {
|
||||
const puzzles = [];
|
||||
for (const subLevel of level.sub_levels) {
|
||||
puzzles.push(...subLevel.subjects);
|
||||
}
|
||||
return Puzzle.find({_id: {$in: puzzles}}).select({attachtype: 1, attach: 1,
|
||||
question: 1, option: 1, answer: 1, extra: 1, tip: 1})
|
||||
.then((topics) => {
|
||||
const topicMap = new Map();
|
||||
for (const topic of topics) {
|
||||
topicMap.set(topic.id, topic);
|
||||
}
|
||||
const resultArr = [];
|
||||
for (const subLevel of level.sub_levels) {
|
||||
const subjects = [];
|
||||
for (const subject of subLevel.subjects) {
|
||||
if (topicMap.has(subject)) {
|
||||
const obj = topicMap.get(subject).toJSON();
|
||||
delete obj['_id'];
|
||||
subjects.push(obj);
|
||||
}
|
||||
}
|
||||
resultArr.push(subjects);
|
||||
}
|
||||
return resultArr;
|
||||
});
|
||||
};
|
||||
export default PuzzleLevelModel;
|
||||
|
11
src/router/index.js
Normal file
11
src/router/index.js
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
import {Router} from 'express';
|
||||
|
||||
import commonRouter from './../controllers/common'
|
||||
|
||||
const router = new Router();
|
||||
|
||||
router.use('/common', commonRouter);
|
||||
|
||||
|
||||
export default router
|
81
src/schedule/weappmsg.schedule.js
Normal file
81
src/schedule/weappmsg.schedule.js
Normal file
@ -0,0 +1,81 @@
|
||||
'use strict';
|
||||
import schedule from 'node-schedule';
|
||||
import logger from '../utils/logger';
|
||||
import wechatUtil from '../utils/wechat.utils';
|
||||
import config from '../../config/config';
|
||||
import WeappFormID from '../models/beagle/WeappFormID';
|
||||
import TemplateMsgRecord from '../models/admin/TemplateMsgRecord';
|
||||
|
||||
|
||||
const msgData = {
|
||||
'touser': '',
|
||||
'weapp_template_msg': {
|
||||
'template_id': config.pay_weapp.message_template_id,
|
||||
'page': '/pages/index',
|
||||
'form_id': '',
|
||||
'data': {
|
||||
'keyword1': {
|
||||
'value': config.pay_weapp.message_value1,
|
||||
},
|
||||
'keyword2': {
|
||||
'value': config.pay_weapp.message_value2,
|
||||
},
|
||||
'keyword3': {
|
||||
'value': config.pay_weapp.message_value3,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const sendOneTemplateMsg = async function(openId, formId, accessToken) {
|
||||
msgData.touser = openId;
|
||||
msgData.weapp_template_msg.form_id = formId;
|
||||
try {
|
||||
await wechatUtil.sendMsgToUser(accessToken, msgData);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
}
|
||||
};
|
||||
export default {
|
||||
async sendAll() {
|
||||
logger.info('开始发送小程序模版消息');
|
||||
try {
|
||||
await WeappFormID.updateExpireTime();
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
}
|
||||
const accessToken = await wechatUtil.getPayAccessToken();
|
||||
const records = await WeappFormID.getUserRecordsDay(config.pay_weapp.app_id);
|
||||
const openIds = records.map((o) => o.open_id);
|
||||
if (records && records.length > 0) {
|
||||
for (const record of records) {
|
||||
try {
|
||||
await sendOneTemplateMsg(record.open_id, record.form_id, accessToken);
|
||||
record.status = 1;
|
||||
record.send_time = new Date();
|
||||
await record.save();
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
const sendRecord = new TemplateMsgRecord({
|
||||
app_id: config.pay_weapp.app_id,
|
||||
open_ids: openIds,
|
||||
});
|
||||
await sendRecord.save();
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
}
|
||||
},
|
||||
/*
|
||||
* 设定每天凌晨8:30:30秒把刷新任务加入队列
|
||||
* */
|
||||
scheduleSendAll() {
|
||||
logger.info('已添加发送小程序模版消息的定时任务');
|
||||
schedule.scheduleJob(config.pay_weapp.schedule_send_time, async () => {
|
||||
this.sendAll();
|
||||
});
|
||||
},
|
||||
};
|
109
src/utils/admin_keeper.js
Normal file
109
src/utils/admin_keeper.js
Normal file
@ -0,0 +1,109 @@
|
||||
import logger from './logger';
|
||||
import Promise from 'bluebird';
|
||||
import {AdminAction, AdminRole} from '../models/admin/Admin';
|
||||
|
||||
const pathPermissions = new Map();
|
||||
const basicActions = new Set();
|
||||
const pathRoles = new Map();
|
||||
const roles = new Map();
|
||||
|
||||
Promise.all([
|
||||
AdminAction.find({deleted: false}).select('_id paths'),
|
||||
AdminRole.find({deleted: false})
|
||||
.select('_id permissions')
|
||||
.populate('permissions', '_id paths'),
|
||||
]).then(([actions, rs]) => {
|
||||
for (const action of actions) {
|
||||
for (const path of action.paths) {
|
||||
const uniPath = path.method + ':' + path.path;
|
||||
if (action._id === '_basic_actions') {
|
||||
basicActions.add(uniPath);
|
||||
} else {
|
||||
if (!pathPermissions.has(uniPath)) {
|
||||
pathPermissions.set(uniPath, new Set());
|
||||
}
|
||||
pathPermissions.get(uniPath).add(action._id);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const role of rs) {
|
||||
if (!roles.has(role.id)) {
|
||||
roles.set(role.id, new Set());
|
||||
}
|
||||
for (const action of role.permissions) {
|
||||
roles.get(role.id).add(action.id);
|
||||
for (const path of action.paths) {
|
||||
const uniPath = path.method + ':' + path.path;
|
||||
if (!pathRoles.has(uniPath)) {
|
||||
pathRoles.set(uniPath, new Set());
|
||||
}
|
||||
pathRoles.get(uniPath).add(role._id);
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.info('load permissions success, NEED REFRESH');
|
||||
}).catch((err) => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
const hasPermission = function(user, path) {
|
||||
let yes = false;
|
||||
if (pathPermissions.has(path)) {
|
||||
const allowedPermissions = pathPermissions.get(path);
|
||||
for (const permission of user.permissions) {
|
||||
if (!yes && allowedPermissions.has(permission)) {
|
||||
yes = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!yes && pathRoles.has(path)) {
|
||||
const allowedRoles = pathRoles.get(path);
|
||||
for (const role of user.roles) {
|
||||
if (!yes && allowedRoles.has(role)) {
|
||||
yes = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return yes;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
|
||||
gatekeeper: function(req, res, next) {
|
||||
const path = req.method + ':' + req.baseUrl + req.path;
|
||||
if (req.isAuthenticated()) {
|
||||
const params = req.method === 'GET' ? req.query : req.body;
|
||||
const ip = req.headers['x-forwarded-for'];
|
||||
logger.info({user: req.user.username, path: path, from: ip, params: params});
|
||||
let mayPass = false;
|
||||
if (basicActions.has(path)) {
|
||||
mayPass = true;
|
||||
} else {
|
||||
mayPass = hasPermission(req.user, path);
|
||||
}
|
||||
|
||||
if (mayPass) {
|
||||
res.locals.lastLogin = req.session.lastLogin;
|
||||
next();
|
||||
} else {
|
||||
const accept = req.headers.accept || '';
|
||||
if (~accept.indexOf('html')) {
|
||||
const err = new Error('您没有权限访问该功能');
|
||||
err.status = 403;
|
||||
throw err;
|
||||
} else {
|
||||
res.status(403).send('您没有权限访问该功能');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (path !== '/logout') {
|
||||
req.session.path_before_login = req.originalUrl;
|
||||
}
|
||||
res.redirect('/login.html');
|
||||
}
|
||||
},
|
||||
};
|
75
src/utils/captcha.js
Normal file
75
src/utils/captcha.js
Normal file
@ -0,0 +1,75 @@
|
||||
'use strict';
|
||||
import svgCaptcha from 'svg-captcha';
|
||||
|
||||
const captchaUtil = {
|
||||
generate_captcha: function() {
|
||||
// const captcha = svgCaptcha.create({
|
||||
// size: 4,
|
||||
// ignoreChars: '0o1i',
|
||||
// });
|
||||
const captcha = svgCaptcha.createMathExpr();
|
||||
return captcha;
|
||||
},
|
||||
|
||||
validate_captcha: function(req, captcha, storedCaptcha, stamp) {
|
||||
if (!storedCaptcha || !captcha) {
|
||||
return false;
|
||||
}
|
||||
if (!stamp) {
|
||||
return false;
|
||||
}
|
||||
if (Date.now() - stamp > 600000) {
|
||||
return false;
|
||||
}
|
||||
return storedCaptcha === captcha.toUpperCase();
|
||||
},
|
||||
|
||||
validate_register_captcha: function(req, captcha) {
|
||||
return captchaUtil.validate_captcha(req, captcha, req.session.captcha_register_mobile,
|
||||
req.session.captcha_register_mobile_stamp);
|
||||
},
|
||||
|
||||
validate_login_captcha: function(req, captcha) {
|
||||
return captchaUtil.validate_captcha(req, captcha, req.session.captcha_login_mobile,
|
||||
req.session.captcha_login_mobile_stamp);
|
||||
},
|
||||
|
||||
validate_findpwd_captcha: function(req, captcha) {
|
||||
return captchaUtil.validate_captcha(req, captcha, req.session.captcha_findpwd_mobile,
|
||||
req.session.captcha_findpwd_mobile_stamp);
|
||||
},
|
||||
|
||||
validate_admin_login_captcha: function(req, captcha) {
|
||||
return captchaUtil.validate_captcha(req, captcha, req.session.captcha_admin_login,
|
||||
req.session.captcha_admin_login_stamp);
|
||||
},
|
||||
|
||||
validate_mobile_code: function(req, stamp, storedMobile, storedCode) {
|
||||
const mobile = req.body.mobile;
|
||||
if (!mobile) {
|
||||
return -1;
|
||||
}
|
||||
const mobileCode = req.body.mobile_code;
|
||||
if (!mobileCode) {
|
||||
return -1;
|
||||
}
|
||||
if (!stamp) {
|
||||
return -2;
|
||||
}
|
||||
if (Date.now() - stamp > 600000) {
|
||||
return -2;
|
||||
}
|
||||
if (!storedMobile) {
|
||||
return -1;
|
||||
}
|
||||
if (!storedCode) {
|
||||
return -1;
|
||||
}
|
||||
if (mobile !== storedMobile || mobileCode !== storedCode) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = captchaUtil;
|
99
src/utils/cdn.utils.js
Normal file
99
src/utils/cdn.utils.js
Normal file
@ -0,0 +1,99 @@
|
||||
import config from '../../config/config';
|
||||
import request from 'request';
|
||||
import Promise from 'bluebird';
|
||||
import stringUtil from './string.utils';
|
||||
import COS from 'cos-nodejs-sdk-v5';
|
||||
|
||||
const generateNonce = function() {
|
||||
return stringUtil.randomNum(10000, 99999);
|
||||
};
|
||||
const generateSign = function(method, data) {
|
||||
let str = `${method}cdn.api.qcloud.com/v2/index.php?`;
|
||||
let i = 0;
|
||||
for (const key in data) {
|
||||
if ({}.hasOwnProperty.call(data, key)) {
|
||||
if (i ++ > 0) str += '&';
|
||||
str += `${key}=${data[key]}`;
|
||||
}
|
||||
}
|
||||
return stringUtil.sha1keyBase64(str, config.cos_cdn.SecretKey);
|
||||
};
|
||||
const cosCDN = new COS({
|
||||
SecretId: config.cos_cdn.SecretId,
|
||||
SecretKey: config.cos_cdn.SecretKey,
|
||||
});
|
||||
export default {
|
||||
|
||||
refreshDir(url) {
|
||||
const now = Math.round(new Date()/1000);
|
||||
const data = {
|
||||
'Action': 'RefreshCdnDir',
|
||||
'Nonce': generateNonce(),
|
||||
'SecretId': config.cos_cdn.SecretId,
|
||||
'Timestamp': now,
|
||||
'dirs.0': url,
|
||||
};
|
||||
data.Signature = generateSign('POST', data);
|
||||
return new Promise((resolve, reject) => {
|
||||
const link = 'https://cdn.api.qcloud.com/v2/index.php';
|
||||
const options = {method: 'POST',
|
||||
url: link,
|
||||
headers: {
|
||||
'Cache-Control': 'no-cache',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
form: data,
|
||||
};
|
||||
request(options, (err, response, body) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(JSON.parse(body));
|
||||
});
|
||||
});
|
||||
},
|
||||
refreshOneUrl(url) {
|
||||
const now = Math.round(new Date()/1000);
|
||||
const data = {
|
||||
'Action': 'RefreshCdnUrl',
|
||||
'Nonce': generateNonce(),
|
||||
'SecretId': config.cos_cdn.SecretId,
|
||||
'Timestamp': now,
|
||||
'urls.0': url,
|
||||
};
|
||||
data.Signature = generateSign('POST', data);
|
||||
return new Promise((resolve, reject) => {
|
||||
const link = 'https://cdn.api.qcloud.com/v2/index.php';
|
||||
const options = {method: 'POST',
|
||||
url: link,
|
||||
headers: {
|
||||
'Cache-Control': 'no-cache',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
form: data,
|
||||
};
|
||||
request(options, (err, response, body) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(JSON.parse(body));
|
||||
});
|
||||
});
|
||||
},
|
||||
uploadToCDN(fileName, path) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
cosCDN.sliceUploadFile({
|
||||
Bucket: 'client-1256832210',
|
||||
Region: 'ap-beijing',
|
||||
Key: fileName,
|
||||
FilePath: path,
|
||||
}, function(err, data) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
51
src/utils/db.util.js
Normal file
51
src/utils/db.util.js
Normal file
@ -0,0 +1,51 @@
|
||||
import mongoose from 'mongoose';
|
||||
import Promise from 'bluebird';
|
||||
import config from '../../config/config';
|
||||
|
||||
|
||||
module.exports = {
|
||||
validatePost: function(req, validate) {
|
||||
for (const rule of validate.rules) {
|
||||
if (rule[1]) {
|
||||
req.checkBody(rule[0], rule[2]).notEmpty();
|
||||
}
|
||||
}
|
||||
return req.validationErrors();
|
||||
},
|
||||
|
||||
leanWithId: function(docs) {
|
||||
for (const doc of docs) {
|
||||
doc.id = String(doc._id);
|
||||
}
|
||||
return docs;
|
||||
},
|
||||
/**
|
||||
* 根据db名,获取连接
|
||||
* @param {string} dbName
|
||||
* @return {Object} dbConnection
|
||||
* */
|
||||
getConnection: function(dbName) {
|
||||
const url = config.db_admin.slice(0, config.db_admin.lastIndexOf('/') + 1) + dbName;
|
||||
return mongoose.createConnection(url, {promiseLibrary: Promise, useNewUrlParser: true});
|
||||
},
|
||||
getConnAdmin: function() {
|
||||
return mongoose.createConnection(config.db_admin, {promiseLibrary: Promise, useNewUrlParser: true});
|
||||
},
|
||||
|
||||
getConnSnoopy: function() {
|
||||
return mongoose.createConnection(config.db_snoopy, {promiseLibrary: Promise, useNewUrlParser: true});
|
||||
},
|
||||
|
||||
getConnDalmatian: function() {
|
||||
return mongoose.createConnection(config.db_dalmatian, {promiseLibrary: Promise, useNewUrlParser: true});
|
||||
},
|
||||
|
||||
getConnBeagle: function() {
|
||||
return mongoose.createConnection(config.db_beagle, {promiseLibrary: Promise, useNewUrlParser: true});
|
||||
},
|
||||
|
||||
getConnGhost: function() {
|
||||
return mongoose.createConnection(config.db_ghost, {promiseLibrary: Promise, useNewUrlParser: true});
|
||||
},
|
||||
|
||||
};
|
58
src/utils/error-utils.js
Normal file
58
src/utils/error-utils.js
Normal file
@ -0,0 +1,58 @@
|
||||
'use strict';
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
module.exports = {
|
||||
format_alert: function(msg) {
|
||||
if (msg) {
|
||||
if (typeof msg === 'string') {
|
||||
return msg;
|
||||
} else if (typeof msg[Symbol.iterator] === 'function') {
|
||||
let msgs = '<ul>';
|
||||
_.each(msg, function(m) {
|
||||
if (typeof m === 'string') {
|
||||
msgs += '<li>' + m + '</li>';
|
||||
} else if (_.has(m, 'msg')) {
|
||||
msgs += '<li>' + m.msg + '</li>';
|
||||
} else if (_.has(m, 'message')) {
|
||||
msgs += '<li>' + m.message + '</li>';
|
||||
} else {
|
||||
msgs += '<li>' + JSON.stringify(m) + '</li>';
|
||||
}
|
||||
});
|
||||
msgs += '</ul>';
|
||||
return msgs;
|
||||
} else {
|
||||
return JSON.stringify(msg);
|
||||
}
|
||||
} else {
|
||||
return msg;
|
||||
}
|
||||
},
|
||||
|
||||
format_notify: function(msg) {
|
||||
if (msg) {
|
||||
if (typeof msg === 'string') {
|
||||
return [msg];
|
||||
} else if (typeof msg[Symbol.iterator] === 'function') {
|
||||
const msgs = [];
|
||||
_.each(msg, function(m) {
|
||||
if (typeof m === 'string') {
|
||||
msgs.push(m);
|
||||
} else if (_.has(m, 'msg')) {
|
||||
msgs.push(m.msg);
|
||||
} else if (_.has(m, 'message')) {
|
||||
msgs.push(m.message);
|
||||
} else {
|
||||
msgs.push(JSON.stringify(m));
|
||||
}
|
||||
});
|
||||
return msgs;
|
||||
} else {
|
||||
return [JSON.stringify(msg)];
|
||||
}
|
||||
} else {
|
||||
return msg;
|
||||
}
|
||||
},
|
||||
};
|
53
src/utils/express-utils.js
Normal file
53
src/utils/express-utils.js
Normal file
@ -0,0 +1,53 @@
|
||||
import errorUtils from './error-utils';
|
||||
|
||||
module.exports = function() {
|
||||
return function(req, res, next) {
|
||||
req.alert = function(msg) {
|
||||
const m = errorUtils.format_alert(msg);
|
||||
if (m) {
|
||||
req.flash('alert', m);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} type one of ['notify', 'success', 'warning', 'error']
|
||||
* @param {string} msg
|
||||
*/
|
||||
req.notify = function(type, msg) {
|
||||
const messages = errorUtils.format_notify(msg);
|
||||
if (messages) {
|
||||
const prefix = 'alertify.' + (type ? type : 'notify') + '(\'';
|
||||
const postfix = '\');';
|
||||
for (const m of messages) {
|
||||
req.flash('notify', prefix + m + postfix);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
res.renderWithValidate = function(template, obj, validate) {
|
||||
const objWithValidate = obj ? obj : {};
|
||||
objWithValidate.validate = validate;
|
||||
res.render(template, objWithValidate);
|
||||
};
|
||||
|
||||
/**
|
||||
* 接口成功返回数据的方法, 统一返回errcode 和 errmsg
|
||||
* @param {Object} data 要返回的数据
|
||||
* */
|
||||
res.successJson = function(data) {
|
||||
data.errcode = 0;
|
||||
data.errmsg = '';
|
||||
res.json(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} errcode 错误代码
|
||||
* @param {string} errmsg 错误消息
|
||||
* */
|
||||
res.errorJson = function(errcode, errmsg) {
|
||||
res.json({errcode, errmsg});
|
||||
};
|
||||
next();
|
||||
};
|
||||
};
|
131
src/utils/file.utils.js
Normal file
131
src/utils/file.utils.js
Normal file
@ -0,0 +1,131 @@
|
||||
'use strict';
|
||||
import fs from 'fs';
|
||||
import crypto from 'crypto';
|
||||
import mime from 'mime-types';
|
||||
import dateformat from 'dateformat';
|
||||
import config from '../../config/config';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
* @param {String} str 待hash的string
|
||||
* @return {String}
|
||||
* */
|
||||
function cryptPwd(str) {
|
||||
const md5 = crypto.createHash('md5');
|
||||
return md5.update(str).digest('hex');
|
||||
}
|
||||
export default {
|
||||
getFileMimeTypeWithCode(typeCode) {
|
||||
let filetype = '';
|
||||
let mimetype;
|
||||
switch (typeCode) {
|
||||
case 'ffd8ffe1':
|
||||
filetype = 'jpg';
|
||||
mimetype = ['image/jpeg', 'image/pjpeg'];
|
||||
break;
|
||||
case '47494638':
|
||||
filetype = 'gif';
|
||||
mimetype = 'image/gif';
|
||||
break;
|
||||
case '89504e47':
|
||||
filetype = 'png';
|
||||
mimetype = ['image/png', 'image/x-png'];
|
||||
break;
|
||||
case '504b34':
|
||||
filetype = 'zip';
|
||||
mimetype = ['application/x-zip', 'application/zip', 'application/x-zip-compressed'];
|
||||
break;
|
||||
case '2f2aae5':
|
||||
filetype = 'js';
|
||||
mimetype = 'application/x-javascript';
|
||||
break;
|
||||
case '2f2ae585':
|
||||
filetype = 'css';
|
||||
mimetype = 'text/css';
|
||||
break;
|
||||
case '5b7bda':
|
||||
filetype = 'json';
|
||||
mimetype = ['application/json', 'text/json'];
|
||||
break;
|
||||
case '3c212d2d':
|
||||
filetype = 'ejs';
|
||||
mimetype = 'text/html';
|
||||
break;
|
||||
case '52494646':
|
||||
filetype = 'webp';
|
||||
mimetype = 'image/webp';
|
||||
break;
|
||||
default:
|
||||
filetype = 'unknown';
|
||||
break;
|
||||
}
|
||||
return {
|
||||
fileType: filetype,
|
||||
mimeType: mimetype,
|
||||
};
|
||||
},
|
||||
isImage(fileType) {
|
||||
return fileType === 'jpg' || fileType === 'jpeg' || fileType === 'png' || fileType === 'gif' || fileType === 'webp';
|
||||
},
|
||||
getFileMimeType: function(filePath) {
|
||||
const buffer = new Buffer(8);
|
||||
const fd = fs.openSync(filePath, 'r');
|
||||
fs.readSync(fd, buffer, 0, 8, 0);
|
||||
const newBuf = buffer.slice(0, 4);
|
||||
const head1 = newBuf[0].toString(16);
|
||||
const head2 = newBuf[1].toString(16);
|
||||
const head3 = newBuf[2].toString(16);
|
||||
const head4 = newBuf[3].toString(16);
|
||||
const typeCode = head1 + head2 + head3 + head4;
|
||||
const result = this.getFileMimeTypeWithCode(typeCode);
|
||||
fs.closeSync(fd);
|
||||
return result;
|
||||
},
|
||||
/* 获取头像路径
|
||||
* wid: 微信id
|
||||
* bid: 机器人id
|
||||
* remote: 是否返回url, 否的话返回本地存储路径
|
||||
* */
|
||||
getAvatarUrl(wid, bid, remote) {
|
||||
const widPath = cryptPwd(wid);
|
||||
let localPath = this.getAvatarPath(bid, remote);
|
||||
localPath += widPath + '.jpg';
|
||||
return localPath;
|
||||
},
|
||||
getAvatarPath(bid, remote) {
|
||||
let localPath = config.upload_to + '/';
|
||||
if (remote) {
|
||||
localPath = config.upload_prefix + '/';
|
||||
}
|
||||
localPath += 'b/'+ bid + '/avatar/';
|
||||
return localPath;
|
||||
},
|
||||
/* 获取图片本地保存路径*/
|
||||
getFilePath() {
|
||||
let localPath = config.upload_to + '/g/';
|
||||
localPath += dateformat('yyyy') + '/';
|
||||
localPath += dateformat('mm') + '/';
|
||||
localPath += dateformat('dd') + '/';
|
||||
return localPath;
|
||||
},
|
||||
/* 获取图片等的本地访问url*/
|
||||
getFileUrl(type) {
|
||||
let localPath = config.upload_prefix + '/g/';
|
||||
localPath += dateformat('yyyy') + '/';
|
||||
localPath += dateformat('mm') + '/';
|
||||
localPath += dateformat('dd') + '/';
|
||||
return localPath;
|
||||
},
|
||||
extension(file) {
|
||||
let ext = mime.extension(file.mimetype);
|
||||
if (ext) {
|
||||
ext = '.' + ext;
|
||||
} else {
|
||||
ext = path.extname(file.originalname);
|
||||
ext = ext ? ext.toLowerCase() : '';
|
||||
}
|
||||
|
||||
return ext;
|
||||
},
|
||||
|
||||
};
|
299
src/utils/gamesvr.utils.js
Normal file
299
src/utils/gamesvr.utils.js
Normal file
@ -0,0 +1,299 @@
|
||||
import request from "request";
|
||||
import Promise from "bluebird";
|
||||
import logger from "./logger";
|
||||
|
||||
const requestData = function(options) {
|
||||
return new Promise((resolve, reject) => {
|
||||
request(options, (err, response, body) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
if (response && response.statusCode === 200) {
|
||||
const data = JSON.parse(body);
|
||||
if (data.errcode) {
|
||||
return reject(new Error(data.errmsg));
|
||||
}
|
||||
resolve(data);
|
||||
} else {
|
||||
logger.error(
|
||||
options,
|
||||
`server response errorCode: ${response && response.statusCode}`
|
||||
);
|
||||
reject(
|
||||
new Error(
|
||||
`server response errorCode: ${response && response.statusCode}`
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
export default {
|
||||
queryScrollList(svr) {
|
||||
const qs = {
|
||||
c: "RollMsg",
|
||||
a: "getMsgList"
|
||||
};
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: qs,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
updateScroll(svr, data) {
|
||||
data.c = "RollMsg";
|
||||
data.a = "updateMsg";
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: data,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
addScroll(svr, data) {
|
||||
data.c = "RollMsg";
|
||||
data.a = "addMsg";
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: data,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
deleteScroll(svr, msgid) {
|
||||
const qs = {
|
||||
c: "RollMsg",
|
||||
a: "removeMsg",
|
||||
msgid: msgid
|
||||
};
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: qs,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
/* begin of mail*/
|
||||
queryMailList(svr) {
|
||||
const qs = {
|
||||
c: "Mail",
|
||||
a: "getMailList"
|
||||
};
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: qs,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
updateMail(svr, data) {
|
||||
data.c = "Mail";
|
||||
data.a = "updateMail";
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: data,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
addMail(svr, data) {
|
||||
data.c = "Mail";
|
||||
data.a = "addMail";
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: data,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
deleteMail(svr, mailid) {
|
||||
const qs = {
|
||||
c: "Mail",
|
||||
a: "deleteMail",
|
||||
mailid: mailid
|
||||
};
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: qs,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
getItemNames(svr, itemIdArr) {
|
||||
const itemIdStr = itemIdArr.join(",");
|
||||
const qs = {
|
||||
c: "Item",
|
||||
a: "getItemsInfo",
|
||||
item_ids: itemIdStr
|
||||
};
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: qs,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
searchItem(svr, { itemId, itemName, start, limit }) {
|
||||
const qs = {
|
||||
c: "Item",
|
||||
a: "searchItem",
|
||||
start: start,
|
||||
limit: limit
|
||||
};
|
||||
if (itemId) qs.item_id = itemId;
|
||||
if (itemName) qs.item_name = itemName;
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: qs,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
/* end of mail*/
|
||||
/* begin of activity*/
|
||||
queryActivityList(svr) {
|
||||
const qs = {
|
||||
c: "Activity",
|
||||
a: "getActivityList"
|
||||
};
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: qs,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
updateActivity(svr, data) {
|
||||
data.c = "Activity";
|
||||
data.a = "updateActivity";
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: data,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
/* end of activity*/
|
||||
|
||||
/* begin of recharge*/
|
||||
queryRechargeList(svr, data) {
|
||||
data.c = "GMTool";
|
||||
data.a = "execSql";
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: data,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
addRecharge(svr, data) {
|
||||
data.c = "GMTool";
|
||||
data.a = "addVirtualOrder";
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: data,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
|
||||
/* end of recharge*/
|
||||
|
||||
/* begin of users*/
|
||||
queryUsersList(svr, data) {
|
||||
data.c = "User";
|
||||
data.a = "searchUser";
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: data,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
updateUser(svr, data) {
|
||||
data.c = "Item";
|
||||
data.a = "searchUser";
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: data,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
// deleteUser(svr, accountid) {
|
||||
// const qs = {
|
||||
// accountid: accountid
|
||||
// };
|
||||
// const options = {
|
||||
// url: svr,
|
||||
// qs: qs,
|
||||
// headers: { "cache-control": "no-cache" }
|
||||
// };
|
||||
// return requestData(options);
|
||||
// },
|
||||
shutupUser(svr, data) {
|
||||
data.c = "User";
|
||||
data.a = "forbidSpeak";
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: data,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
forbidUser(svr, data) {
|
||||
data.c = "User";
|
||||
data.a = "forbidAccount";
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: data,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
disforbidUser(svr, data) {
|
||||
data.c = "User";
|
||||
data.a = "disforbidAccount";
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: data,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
disforbidSpeak(svr, data) {
|
||||
data.c = "User";
|
||||
data.a = "forbidSpeak";
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: data,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
queryForbid(svr, data) {
|
||||
data.c = "User";
|
||||
data.a = "searchForbidAccount";
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: data,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
queryShutup(svr, data) {
|
||||
data.c = "User";
|
||||
data.a = "searchForbidSpeak";
|
||||
const options = {
|
||||
url: svr,
|
||||
qs: data,
|
||||
headers: { "cache-control": "no-cache" }
|
||||
};
|
||||
return requestData(options);
|
||||
},
|
||||
|
||||
|
||||
/* end of users*/
|
||||
|
||||
};
|
21
src/utils/gm-logs.js
Normal file
21
src/utils/gm-logs.js
Normal file
@ -0,0 +1,21 @@
|
||||
export default {
|
||||
type: {
|
||||
servers: "服务器管理",
|
||||
announces: "公告",
|
||||
scroll_texts: "跑马灯",
|
||||
mails: "邮件",
|
||||
activity: "活动",
|
||||
list: "兑换码",
|
||||
gift_list: "礼包",
|
||||
recharge_record: "充值记录",
|
||||
recharge_request: "充值请求",
|
||||
users: "用户管理",
|
||||
chat_logs: "聊天信息",
|
||||
op_logs: "操作记录"
|
||||
},
|
||||
abstractType(path) {
|
||||
const reg = /\/gm\/(.+).html/g;
|
||||
const result = reg.exec(path);
|
||||
return result ? result[1] : '';
|
||||
}
|
||||
};
|
122
src/utils/logger.js
Normal file
122
src/utils/logger.js
Normal file
@ -0,0 +1,122 @@
|
||||
'use strict';
|
||||
import fs from 'fs-extra';
|
||||
import FileStreamRotator from 'file-stream-rotator';
|
||||
import bunyan from 'bunyan';
|
||||
import config from '../../config/config';
|
||||
import AdminLog from '../models/admin/AdminLog';
|
||||
|
||||
|
||||
const env = process.env.NODE_ENV || 'development';
|
||||
const isDev = env === 'development';
|
||||
|
||||
const logDir = config.logs_path;
|
||||
fs.existsSync(logDir) || fs.mkdirSync(logDir);
|
||||
|
||||
let logger = null;
|
||||
const createLogger = function(appName) {
|
||||
appName = !appName ? config.app.name : appName;
|
||||
const streams = [{
|
||||
level: 'info',
|
||||
stream: FileStreamRotator.getStream({
|
||||
date_format: 'YYYYMMDD',
|
||||
filename: `${logDir}/${appName}-%DATE%.log`,
|
||||
frequency: 'daily',
|
||||
verbose: false,
|
||||
}),
|
||||
}];
|
||||
if (isDev) {
|
||||
streams.push({
|
||||
level: 'debug',
|
||||
stream: process.stdout,
|
||||
});
|
||||
}
|
||||
return bunyan.createLogger({
|
||||
name: appName,
|
||||
serializers: bunyan.stdSerializers,
|
||||
streams: streams,
|
||||
src: false,
|
||||
});
|
||||
};
|
||||
export default {
|
||||
info(obj, msg) {
|
||||
if (!logger) {
|
||||
logger = createLogger(global.app_name);
|
||||
}
|
||||
if (msg) {
|
||||
logger.info(obj, msg);
|
||||
} else {
|
||||
logger.info(obj);
|
||||
}
|
||||
},
|
||||
error(obj, msg) {
|
||||
if (!logger) {
|
||||
logger = createLogger(global.app_name);
|
||||
}
|
||||
if (msg) {
|
||||
logger.error(obj, msg);
|
||||
} else {
|
||||
logger.error(obj);
|
||||
}
|
||||
},
|
||||
warn(obj, msg) {
|
||||
if (!logger) {
|
||||
logger = createLogger(global.app_name);
|
||||
}
|
||||
if (msg) {
|
||||
logger.warn(obj, msg);
|
||||
} else {
|
||||
logger.warn(obj);
|
||||
}
|
||||
},
|
||||
debug(obj, msg) {
|
||||
if (!logger) {
|
||||
logger = createLogger(global.app_name);
|
||||
}
|
||||
if (msg) {
|
||||
logger.debug(obj, msg);
|
||||
} else {
|
||||
logger.debug(obj);
|
||||
}
|
||||
},
|
||||
trace(obj, msg) {
|
||||
if (!logger) {
|
||||
logger = createLogger(global.app_name);
|
||||
}
|
||||
if (msg) {
|
||||
logger.trace(obj, msg);
|
||||
} else {
|
||||
logger.trace(obj);
|
||||
}
|
||||
},
|
||||
fatal(obj, msg) {
|
||||
if (!logger) {
|
||||
logger = createLogger(global.app_name);
|
||||
}
|
||||
if (msg) {
|
||||
logger.fatal(obj, msg);
|
||||
} else {
|
||||
logger.fatal(obj);
|
||||
}
|
||||
},
|
||||
db(req, logObj, name) {
|
||||
const user = req.user;
|
||||
const ip = req.headers['x-forwarded-for'];
|
||||
const path = req.baseUrl + req.path;
|
||||
const params = req.method === 'GET' ? req.query : req.body;
|
||||
const dataObj = JSON.stringify(logObj) === '{}' ? params : logObj;
|
||||
const obj = new AdminLog({
|
||||
admin: user.id,
|
||||
username: user.username,
|
||||
path: path,
|
||||
method: req.method,
|
||||
params: dataObj,
|
||||
referer: req.headers['referer'],
|
||||
user_agent: req.headers['user-agent'],
|
||||
ip: ip,
|
||||
show_name: name,
|
||||
});
|
||||
obj.save().then(()=>{}).catch((err)=> {
|
||||
logger.error(err);
|
||||
});
|
||||
},
|
||||
};
|
236
src/utils/string.utils.js
Normal file
236
src/utils/string.utils.js
Normal file
@ -0,0 +1,236 @@
|
||||
'use strict';
|
||||
import crypto from 'crypto';
|
||||
import format from 'biguint-format';
|
||||
|
||||
const chnNumChar = {
|
||||
零: 0,
|
||||
一: 1,
|
||||
二: 2,
|
||||
三: 3,
|
||||
四: 4,
|
||||
五: 5,
|
||||
六: 6,
|
||||
七: 7,
|
||||
八: 8,
|
||||
九: 9,
|
||||
};
|
||||
const chnNameValue = {
|
||||
十: {value: 10, secUnit: false},
|
||||
百: {value: 100, secUnit: false},
|
||||
千: {value: 1000, secUnit: false},
|
||||
万: {value: 10000, secUnit: true},
|
||||
亿: {value: 100000000, secUnit: true},
|
||||
};
|
||||
export default {
|
||||
/**
|
||||
* 判断传入的值是否为true
|
||||
* @param {string} obj 传入值为'true','TRUE',1,'1','on','ON','YES','yes'时,返回true,其他值均返回false
|
||||
* @return {boolean}
|
||||
*/
|
||||
isTrue(obj) {
|
||||
return obj === 'true' || obj === 'TRUE' || obj === 'on' || obj === 'ON' || obj === true || obj === 1
|
||||
|| obj === '1' || obj === 'YES' || obj === 'yes';
|
||||
},
|
||||
isNull(obj) {
|
||||
return !obj || obj === 'null' || obj === 'NULL' || obj === '' || obj === 'undefined';
|
||||
},
|
||||
isObjNull(obj) {
|
||||
return !obj || JSON.stringify(obj) === '{}';
|
||||
},
|
||||
splitString(text, separator, length) {
|
||||
const list = [];
|
||||
let lastIndex = 0;
|
||||
for (let i = 0; i < length - 1; i++) {
|
||||
const j = text.indexOf(separator, lastIndex);
|
||||
if (j === -1) {
|
||||
break;
|
||||
} else {
|
||||
list.push(text.slice(lastIndex, j));
|
||||
lastIndex = j + 1;
|
||||
}
|
||||
}
|
||||
list.push(text.slice(lastIndex, text.length));
|
||||
return list;
|
||||
},
|
||||
md5(text) {
|
||||
return crypto.createHash('md5').update(this.toBuffer(text)).digest('hex');
|
||||
},
|
||||
sha1(str) {
|
||||
return crypto.createHash('sha1').update(str).digest('hex');
|
||||
},
|
||||
sha1keyBase64(str, key) {
|
||||
return crypto.createHmac('sha1', key).update(str).digest('base64');
|
||||
},
|
||||
toBuffer(data) {
|
||||
if (Buffer.isBuffer(data)) return data;
|
||||
if (typeof data === 'string') return new Buffer(data);
|
||||
throw new Error('invalid data type, must be string or buffer');
|
||||
},
|
||||
integerToShortString(value) {
|
||||
return Number(value).toString(36);
|
||||
},
|
||||
isMatch(str, regStr) {
|
||||
const re = new RegExp(regStr, 'gi');
|
||||
return re.test(str);
|
||||
},
|
||||
/* 用正则表达式实现html转码*/
|
||||
htmlEncodeByRegExp: function(str) {
|
||||
let s = '';
|
||||
if (str.length === 0) return '';
|
||||
s = str.replace(/&/g, '&');
|
||||
s = s.replace(/</g, '<');
|
||||
s = s.replace(/>/g, '>');
|
||||
s = s.replace(/ /g, ' ');
|
||||
s = s.replace(/'/g, ''');
|
||||
s = s.replace(/"/g, '"');
|
||||
return s;
|
||||
},
|
||||
/* 用正则表达式实现html解码*/
|
||||
htmlDecodeByRegExp: function(str) {
|
||||
let s = '';
|
||||
if (str.length === 0) return '';
|
||||
s = str.replace(/&/g, '&');
|
||||
s = s.replace(/</g, '<');
|
||||
s = s.replace(/>/g, '>');
|
||||
s = s.replace(/ /g, ' ');
|
||||
s = s.replace(/'/g, '\'');
|
||||
s = s.replace(/"/g, '"');
|
||||
return s;
|
||||
},
|
||||
removeHtml(content, replceEnter) {
|
||||
if (replceEnter) {
|
||||
return content.replace(/<.+?>/g, '').replace(/\r\n/g, '<br/>').replace(/\s/g, '')
|
||||
.replace(/(<br\/>)+/g, '<br/>').replace(/^<br\/>/, '').replace(/<br\/>$/, '');
|
||||
} else {
|
||||
return content.replace(/<.+?>/g, '').replace(/\s/g, '');
|
||||
}
|
||||
},
|
||||
parseUrlObj(content) {
|
||||
content = this.htmlDecodeByRegExp(content);
|
||||
const re = /^.+?<title>(.+?)<\/title><des>(.+?)<\/des>.+?<url>(.+?)<\/url>.+?<thumburl>(.*?)<\/thumburl>.+?$/;
|
||||
const contents = content.match(re);
|
||||
const urlObj = {};
|
||||
if (contents) {
|
||||
if (contents.length > 1) urlObj.title = contents[1];
|
||||
if (contents.length > 2) urlObj.desc = contents[2];
|
||||
if (contents.length > 3) urlObj.url = contents[3];
|
||||
if (contents.length > 4) urlObj.thumburl = contents[4];
|
||||
}
|
||||
return urlObj;
|
||||
},
|
||||
parseFileObj(content) {
|
||||
content = this.htmlDecodeByRegExp(content);
|
||||
const re = /^.+?<title>(.+?)<\/title>.+?<totallen>(.+?)<\/totallen>.+?<fileext>(.*?)<\/fileext>.+?$/;
|
||||
|
||||
const contents = content.match(re);
|
||||
const fileObj = {};
|
||||
if (contents) {
|
||||
if (contents.length > 1) fileObj.title = contents[1];
|
||||
if (contents.length > 2) fileObj.totallen = contents[2];
|
||||
if (contents.length > 3) fileObj.fileext = contents[3];
|
||||
}
|
||||
return fileObj;
|
||||
},
|
||||
string10to62(number) {
|
||||
const chars = '0123456789abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ'.split('');
|
||||
const radix = chars.length;
|
||||
let qutient = +number;
|
||||
const arr = [];
|
||||
do {
|
||||
const mod = qutient % radix;
|
||||
qutient = (qutient - mod) / radix;
|
||||
arr.unshift(chars[mod]);
|
||||
} while (qutient);
|
||||
return arr.join('');
|
||||
},
|
||||
string62to10(numberCode) {
|
||||
const chars = '0123456789abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ';
|
||||
const radix = chars.length;
|
||||
numberCode = String(numberCode);
|
||||
const len = numberCode.length;
|
||||
let i = 0;
|
||||
let originNumber = 0;
|
||||
while (i < len) {
|
||||
originNumber += Math.pow(radix, i++) * chars.indexOf(numberCode.charAt(len - i) || 0);
|
||||
}
|
||||
return originNumber;
|
||||
},
|
||||
checkWithRes(content, res) {
|
||||
let results = null;
|
||||
res.some((re) => !!(results = content.match(re)));
|
||||
return !(results === null || results.length === 0);
|
||||
},
|
||||
/* 移除微信图文消息中不支持的字符*/
|
||||
replaceWxUnSupportChar(str) {
|
||||
return str.replace(/&/g, '%26');
|
||||
},
|
||||
isArray(object) {
|
||||
return object && typeof object === 'object' && Array === object.constructor;
|
||||
},
|
||||
/* 中文数字转number*/
|
||||
chineseToNumber(chnStr) {
|
||||
let rtn = 0;
|
||||
let section = 0;
|
||||
let number = 0;
|
||||
let secUnit = false;
|
||||
const str = chnStr.split('');
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const num = chnNumChar[str[i]];
|
||||
if (typeof num !== 'undefined') {
|
||||
number = num;
|
||||
if (i === str.length - 1) {
|
||||
section += number;
|
||||
}
|
||||
} else {
|
||||
const unit = chnNameValue[str[i]].value;
|
||||
secUnit = chnNameValue[str[i]].secUnit;
|
||||
if (secUnit) {
|
||||
section = (section + number) * unit;
|
||||
rtn += section;
|
||||
section = 0;
|
||||
} else {
|
||||
section += (number * unit);
|
||||
}
|
||||
number = 0;
|
||||
}
|
||||
}
|
||||
return rtn + section;
|
||||
},
|
||||
getReqRemoteIp(req) {
|
||||
const ip = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/;
|
||||
const address = (req.headers['x-forwarded-for'] || '').split(',')[0] || req.ip;
|
||||
const results = ip.exec(address);
|
||||
return !results && results.length > 0 ? '' : results[0];
|
||||
},
|
||||
randomNum(minNum, maxNum) {
|
||||
return parseInt(Math.random()*(maxNum-minNum+1)+minNum, 10);
|
||||
},
|
||||
randomNumAdv(minNum, maxNum) {
|
||||
const x = format(crypto.randomBytes(4), 'dec');
|
||||
return parseInt(x / Math.pow(2, 4 * 8) * (maxNum + 1 - minNum) + minNum);
|
||||
},
|
||||
string10to36(number) {
|
||||
const chars = '0123456789abcdefghigklmnopqrstuvwxyz'.split('');
|
||||
const radix = chars.length;
|
||||
let qutient = +number;
|
||||
const arr = [];
|
||||
do {
|
||||
const mod = qutient % radix;
|
||||
qutient = (qutient - mod) / radix;
|
||||
arr.unshift(chars[mod]);
|
||||
} while (qutient);
|
||||
return arr.join('');
|
||||
},
|
||||
string36to10(numberCode) {
|
||||
const chars = '0123456789abcdefghigklmnopqrstuvwxyz';
|
||||
const radix = chars.length;
|
||||
numberCode = String(numberCode);
|
||||
const len = numberCode.length;
|
||||
let i = 0;
|
||||
let originNumber = 0;
|
||||
while (i < len) {
|
||||
originNumber += Math.pow(radix, i++) * chars.indexOf(numberCode.charAt(len - i) || 0);
|
||||
}
|
||||
return originNumber;
|
||||
},
|
||||
};
|
128
src/utils/wechat.utils.js
Normal file
128
src/utils/wechat.utils.js
Normal file
@ -0,0 +1,128 @@
|
||||
import config from '../../config/config';
|
||||
import request from 'request';
|
||||
import Promise from 'bluebird';
|
||||
import fs from 'fs';
|
||||
import logger from './logger';
|
||||
|
||||
const refreshToken = function(appId, appSecret) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const link =
|
||||
`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appId}&secret=${appSecret}`;
|
||||
request(link, (err, response, body) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
const data = JSON.parse(body);
|
||||
if (data.errcode) {
|
||||
return reject(new Error(data.errmsg));
|
||||
}
|
||||
resolve(data.access_token);
|
||||
});
|
||||
});
|
||||
};
|
||||
export default {
|
||||
/**
|
||||
* 获取支付小程序的access token
|
||||
* 如果global中有token,且未过去(超过2个小时), 则直接返回
|
||||
* 否则刷新access token。
|
||||
* 如果globa中有token,且时间不到5分钟,则先返回token,然后刷新token
|
||||
* @return {Promise}
|
||||
*/
|
||||
getPayAccessToken() {
|
||||
const appId = config.pay_weapp.app_id;
|
||||
const appSecret = config.pay_weapp.app_secret;
|
||||
return new Promise((resolve, reject) => {
|
||||
const now = Math.round(new Date() / 1000);
|
||||
if (global.accessToken && now < global.tokenExpireTime) {
|
||||
resolve(global.accessToken);
|
||||
if (global.tokenExpireTime - now < 5 * 60) {
|
||||
process.nextTick(function() {
|
||||
refreshToken(appId, appSecret)
|
||||
.then(() => {
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error('refresh access token error');
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
refreshToken(appId, appSecret)
|
||||
.then((token) => {
|
||||
resolve(token);
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
/* 微信登录凭证校验*/
|
||||
codeToSession(code) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const link =
|
||||
`https://api.weixin.qq.com/sns/jscode2session?
|
||||
appid=${config.weapp.app_id}&secret=${config.weapp.app_secret}&js_code=${code}&grant_type=authorization_code`;
|
||||
request(link, (err, response, body) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
const data = JSON.parse(body);
|
||||
const sessionKey = data.session_key;
|
||||
const openId = data.openid;
|
||||
resolve({openId, sessionKey});
|
||||
});
|
||||
});
|
||||
},
|
||||
sendMsgToUser(accessToken, data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const link = `https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=${accessToken}`;
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: link,
|
||||
headers: {
|
||||
'Cache-Control': 'no-cache',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: data,
|
||||
json: true,
|
||||
};
|
||||
request(options, (err, response, body) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(body);
|
||||
});
|
||||
});
|
||||
},
|
||||
generateQr(appId, appSecret, scene, filePath) {
|
||||
const stream = fs.createWriteStream(filePath);
|
||||
return new Promise((resolve, reject) => {
|
||||
refreshToken(appId, appSecret)
|
||||
.then((token) => {
|
||||
const link = `https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=${token}`;
|
||||
const data = {
|
||||
scene: scene,
|
||||
width: 430,
|
||||
auto_color: false,
|
||||
line_color: {'r': '0', 'g': '0', 'b': '0'},
|
||||
};
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: link,
|
||||
headers: {
|
||||
'Cache-Control': 'no-cache',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: data,
|
||||
json: true,
|
||||
};
|
||||
request(options)
|
||||
.pipe(stream)
|
||||
.on('close', resolve);
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
62
test/test.js
Normal file
62
test/test.js
Normal file
@ -0,0 +1,62 @@
|
||||
var express = require('express');
|
||||
var ldap = require('ldapjs');
|
||||
|
||||
var app = express();
|
||||
|
||||
//创建LDAP client,把服务器url传入
|
||||
var client = ldap.createClient({
|
||||
url: 'ldap://ldap.kingsome.cn:389'
|
||||
});
|
||||
|
||||
//创建LDAP查询选项
|
||||
//filter的作用就是相当于SQL的条件
|
||||
var opts = {
|
||||
filter: '(uid=yulixing)', //查询条件过滤器,查找uid=kxh的用户节点
|
||||
scope: 'sub', //查询范围
|
||||
timeLimit: 500 //查询超时
|
||||
};
|
||||
|
||||
var user = [];
|
||||
app.get('/', function(req, res, next) {
|
||||
//将client绑定LDAP Server
|
||||
//第一个参数:是用户,必须是从根节点到用户节点的全路径
|
||||
//第二个参数:用户密码
|
||||
client.bind('cn=admin,dc=kingsome,dc=cn', 'milesQWE321', function(err, res1) {
|
||||
//开始查询
|
||||
//第一个参数:查询基础路径,代表在查询用户信心将在这个路径下进行,这个路径是由根节开始
|
||||
//第二个参数:查询选项
|
||||
client.search('ou=people,dc=kingsome,dc=cn', opts, function(err, res2) {
|
||||
console.log(res2)
|
||||
//查询结果事件响应
|
||||
res2.on('searchEntry', function(entry) {
|
||||
//获取查询的对象
|
||||
var user = entry.object;
|
||||
var userText = JSON.stringify(user, null, 2);
|
||||
users = entry
|
||||
// console.log(entry)
|
||||
// console.log(userText);
|
||||
});
|
||||
|
||||
res2.on('searchReference', function(referral) {
|
||||
console.log('referral: ' + referral.uris.join());
|
||||
});
|
||||
|
||||
//查询错误事件
|
||||
res2.on('error', function(err) {
|
||||
console.error('error: ' + err.message);
|
||||
//unbind操作,必须要做
|
||||
client.unbind();
|
||||
});
|
||||
|
||||
//查询结束
|
||||
res2.on('end', function(result) {
|
||||
console.log('search status: ' + result);
|
||||
//unbind操作,必须要做
|
||||
client.unbind();
|
||||
});
|
||||
res.send({})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.listen('6789');
|
88
test/test2.js
Normal file
88
test/test2.js
Normal file
@ -0,0 +1,88 @@
|
||||
var express = require('express');
|
||||
var ldap = require('ldapjs');
|
||||
|
||||
var app = express();
|
||||
|
||||
//创建LDAP client,把服务器url传入
|
||||
var client = ldap.createClient({
|
||||
url: 'ldap://ldap.kingsome.cn:389'
|
||||
});
|
||||
|
||||
//创建LDAP查询选项
|
||||
//filter的作用就是相当于SQL的条件
|
||||
var opts = {
|
||||
// filter: '(objectClass=posixAccount)', //查询条件过滤器,查找uid=kxh的用户节点
|
||||
filter: '(uid=yulixing1)', //查询条件过滤器,查找uid=kxh的用户节点
|
||||
scope: 'sub', //查询范围
|
||||
timeLimit: 500 //查询超时
|
||||
};
|
||||
|
||||
var user = [];
|
||||
app.get('/', function(req, res, next) {
|
||||
//将client绑定LDAP Server
|
||||
//第一个参数:是用户,必须是从根节点到用户节点的全路径
|
||||
//第二个参数:用户密码
|
||||
client.bind('cn=admin,dc=kingsome,dc=cn', 'milesQWE321', function(err, res1) {
|
||||
console.log(err);
|
||||
//开始查询
|
||||
//第一个参数:查询基础路径,代表在查询用户信心将在这个路径下进行,这个路径是由根节开始
|
||||
//第二个参数:查询选项
|
||||
client.search('ou=people,dc=kingsome,dc=cn', opts, function(err, res2) {
|
||||
var entries = [];
|
||||
//查询结果事件响应
|
||||
res2.on('searchEntry', function(entry) {
|
||||
//获取查询的对象
|
||||
var user = entry.object;
|
||||
entries.push(user);
|
||||
users = entry;
|
||||
});
|
||||
|
||||
res2.on('searchReference', function(referral) {
|
||||
console.log('referral: ' + referral.uris.join());
|
||||
});
|
||||
|
||||
//查询错误事件
|
||||
res2.on('error', function(err) {
|
||||
console.error('error: ' + err.message);
|
||||
//unbind操作,必须要做
|
||||
client.unbind();
|
||||
});
|
||||
|
||||
//查询结束
|
||||
res2.on('end', function(result) {
|
||||
console.log('search status: ' + result);
|
||||
console.log(entries)
|
||||
if (entries.length !== 0) {
|
||||
client.bind(entries[0].dn, 'yulixing123456', function(
|
||||
err,
|
||||
res3
|
||||
) {
|
||||
if (err) {
|
||||
res.send({
|
||||
err: err,
|
||||
errmsg: err.message
|
||||
|
||||
})
|
||||
} else {
|
||||
res.send({
|
||||
result: entries,
|
||||
state: 0
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
res.send({
|
||||
msg: '登录失败'
|
||||
})
|
||||
}
|
||||
// res.send({
|
||||
// entries
|
||||
// })
|
||||
//unbind操作,必须要做
|
||||
client.unbind();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.listen('6789');
|
Loading…
x
Reference in New Issue
Block a user