219 lines
7.2 KiB
JavaScript
219 lines
7.2 KiB
JavaScript
'use strict';
|
|
const bunyan = require('bunyan');
|
|
const PrettyStream = require('bunyan-pretty-colors');
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
|
|
const prettyStdOut = new PrettyStream();
|
|
|
|
if (!fs.existsSync('minelog')) {
|
|
fs.mkdirSync('minelog');
|
|
}
|
|
const logger = function (option) {
|
|
option = option || {};
|
|
return bunyan.createLogger({
|
|
name: "log",
|
|
src: false,
|
|
streams: [
|
|
{
|
|
level: 'info',
|
|
stream: prettyStdOut
|
|
},
|
|
{
|
|
level: 'info',
|
|
type: 'rotating-file',
|
|
path: path.join(option.root, option.file || 'info.log'),
|
|
period: '1d', // daily rotation
|
|
count: 365 // keep 30 days
|
|
}
|
|
]
|
|
});
|
|
};
|
|
const filename = 'minelog' + new Date().getFullYear() + new Date().getMonth() + new Date().getDate();
|
|
const mylog = logger(({ root: 'minelog', file: filename }));
|
|
|
|
const mongoose = require('mongoose');
|
|
const User = require('../modules/user/user.model');
|
|
const Fund = require('../modules/fund/fund.model');
|
|
const system = require('../modules/system/system.model');
|
|
const { createAction } = require('../modules/action/action.handler');
|
|
const revenueLadderSheet = [
|
|
{amount: 10000, rate: 0.001},
|
|
{amount: 40000, rate: 0.003},
|
|
{amount: 50000, rate: 0.006},
|
|
{amount: 100000, rate: 0.01},
|
|
{amount: 300000, rate: 0.015},
|
|
{amount: 500000, rate: 0.018},
|
|
{amount: NaN, rate: 0.02}
|
|
];
|
|
|
|
const probe = {
|
|
currentIndex: 0
|
|
};
|
|
|
|
function awardMine(fund) {
|
|
if (!fund || fund.vicFundSum === 0) return 0;
|
|
let rest = +fund.vicFundSum;
|
|
let revenue = 0;
|
|
for (let level of revenueLadderSheet) {
|
|
if (rest - level.amount > 0) {
|
|
revenue += level.amount * level.rate;
|
|
rest -= level.amount;
|
|
} else {
|
|
revenue += rest * level.rate;
|
|
break;
|
|
}
|
|
}
|
|
return revenue;
|
|
}
|
|
// fund.option.usedGroupReward : 已领取的社群奖励总额
|
|
const doReward = async (user, fund) => {
|
|
if (!user || !fund) return 0;
|
|
const rewardMine = awardMine(fund);
|
|
if (rewardMine === 0) return 0;
|
|
mylog.info('挖矿奖励: ', rewardMine);
|
|
rewardMine && await Fund.findByIdAndUpdate(fund.id, {
|
|
$inc: {
|
|
vicFundSum: rewardMine,
|
|
},
|
|
$set: {
|
|
lastMine: new Date().toISOString()
|
|
}
|
|
}).then(async () => {
|
|
rewardMine > 0 && createAction(1, {
|
|
userId: user.id,
|
|
tokenType: 'vic2vic', // 新模式标记
|
|
amount: rewardMine,
|
|
remain: fund.asset
|
|
}).save();
|
|
if (user.inviter) {
|
|
const inviter = await Fund.findOne({ userId: user.inviter });
|
|
if (inviter && +inviter.vicFundSum >= 10000) {
|
|
const rewardInviter = rewardMine * 0.1;
|
|
Promise.all([
|
|
Fund.findOneAndUpdate({ userId: user.inviter }, {
|
|
$inc: {
|
|
vicFundSum: rewardInviter
|
|
}
|
|
}),
|
|
createAction(4, {
|
|
userId: user.inviter,
|
|
tokenType: 'vic2vic', // 新模式标记
|
|
amount: rewardInviter,
|
|
remain: fund.asset,
|
|
detail: { level: 1, info: '1级奖励' }
|
|
}).save()
|
|
]).catch((err) => mylog.error(err.errmsg));
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
const mock = {
|
|
touch: () => { mylog.info('-----------------\n'); },
|
|
done: () => { mylog.info('done'); },
|
|
};
|
|
|
|
async function mine (job = mock, done = mock) {
|
|
//todo: 程序内检查是否可以执行,保证不会启动时错误的自动执行一次
|
|
mylog.info('[Schedule Job] 挖矿程序 开始定时计算作业...');
|
|
let currentIndex = await system.findOne({
|
|
key: 'userIndex4job'
|
|
}).exec();
|
|
currentIndex = currentIndex.value;
|
|
if (typeof currentIndex !== 'number') {
|
|
currentIndex = 0;
|
|
await new system({
|
|
key: 'userIndex4job',
|
|
value: currentIndex
|
|
}).save();
|
|
}
|
|
let allFundNum = await Fund.countDocuments();
|
|
await system.updateOne({
|
|
key: 'restFundNum',
|
|
}, {
|
|
value: allFundNum
|
|
});
|
|
// 批量更新,每次十个
|
|
for (; currentIndex < allFundNum;) {
|
|
const userFundList = await Fund.find({ vicFundSum: { $gt: 0 } }).sort('_id').limit(10).skip(currentIndex);
|
|
typeof job.touch === 'function' && job.touch();
|
|
if (!userFundList || !Array.isArray(userFundList)) return 0;
|
|
for (let i = 0, len = userFundList.length; i < len; ++i) {
|
|
const fund = userFundList[i]; // 一条数据库fund表记录
|
|
// if (fund.lastMine && new Date(fund.lastMine).getDate() === new Date().getDate()) {
|
|
// mylog.info('已经计算过...跳过');
|
|
// continue;
|
|
// }
|
|
const user = await User.findById(fund.userId).exec();
|
|
mylog.info('No. ', currentIndex, ' - ', i);
|
|
mylog.info('开始更新用户: ', user.id);
|
|
await doReward(user, fund);
|
|
mylog.info('用户: ', user.id, '更新完毕\n');
|
|
typeof job.touch === 'function' && job.touch();
|
|
await system.updateOne({ key: 'userIndex4job' }, {
|
|
$set: {
|
|
value: currentIndex
|
|
}
|
|
});
|
|
await system.updateOne({ key: 'restFundNum' }, {
|
|
$inc: {
|
|
value: -1
|
|
}
|
|
});
|
|
}
|
|
currentIndex += 10;
|
|
probe.currentIndex = currentIndex;
|
|
}
|
|
mylog.info('收益计算分配完成');
|
|
await system.findOneAndUpdate({
|
|
key: 'userIndex4job'
|
|
}, { $set: { value: 0 } }).exec();
|
|
await system.findOneAndUpdate({
|
|
key: 'mineCount',
|
|
}, {
|
|
$inc: {
|
|
value: 1
|
|
}
|
|
}, { upsert: true });
|
|
await system.findOneAndUpdate({
|
|
key: 'lastMine',
|
|
}, {
|
|
value: new Date().toISOString()
|
|
}, { upsert: true });
|
|
typeof done === 'function' && done('收益计算分配完成');
|
|
process.exit();
|
|
}
|
|
|
|
const start = async (job, done) => {
|
|
const SysConfig = require('../../configSec');
|
|
mongoose.set('useCreateIndex', true);
|
|
const { DB_USER_NAME, DB_PASSWD, DB_HOST, DB_PORT, DB_NAME } = SysConfig;
|
|
const db = mongoose.connection;
|
|
db.on('error', mylog.error);
|
|
db.once('open', function() {
|
|
mylog.info('数据库连接成功...');
|
|
});
|
|
mylog.info('开始连接数据库...');
|
|
await mongoose.connect(`mongodb://${DB_USER_NAME}:${DB_PASSWD}@${DB_HOST}:${DB_PORT}/${DB_NAME}`, {
|
|
useNewUrlParser: true,
|
|
bufferMaxEntries: 0,
|
|
autoReconnect: true,
|
|
useFindAndModify: false,
|
|
useUnifiedTopology: true
|
|
});
|
|
const beforeExit = async (msg) => {
|
|
mylog.info('意外退出', msg);
|
|
mylog.info('进行到第'+ probe.currentIndex + '个用户');
|
|
process.exit(0);
|
|
};
|
|
process.on('SIGINT', beforeExit);
|
|
process.on('uncaughtException', beforeExit);
|
|
process.on('unhandledRejection', beforeExit);
|
|
typeof process.send === 'function' && process.send('子进程开始执行mine');
|
|
mine(job, done);
|
|
typeof process.send === 'function' && process.send('子进程mine执行完毕');
|
|
};
|
|
|
|
start();
|