190 lines
6.6 KiB
JavaScript
190 lines
6.6 KiB
JavaScript
'use strict';
|
||
/**
|
||
* 为何要把分属于不同业务模块的job集中到此处?
|
||
* 如果后续要把任务执行与web服务器分离,就会很方便,就算不分离,也没多大坏处。
|
||
|
||
* 任务包括:
|
||
* transaction : {
|
||
* 1. 每小时去清理指定startTime的地产,将
|
||
* {
|
||
* status: -> 1,
|
||
* price: -> price * (1 + profit),
|
||
* currentOwner: -> ''
|
||
* }
|
||
* 2. 将currentOwner对应的用户的asset.log加上a
|
||
* }
|
||
*/
|
||
const mongoose = require('mongoose');
|
||
const User = require('../modules/user/user.model');
|
||
const Fund = require('../modules/fund/fund.model');
|
||
const Action = require('../modules/action/action.model');
|
||
const system = require('../modules/system/system.model');
|
||
const { createAction } = require('../modules/action/action.handler');
|
||
const { createNewFund } = require('../modules/fund/fund.handler');
|
||
|
||
const tokenReleaseRate = 0.02;
|
||
async function awardMine(user, fund) {
|
||
if (!user || !fund || fund.fundList.length === 0) return 0;
|
||
let sum = 0;
|
||
for (let i = 0, len = fund.fundList.length; i < len; ++i) {
|
||
const released = fund.releasedAmout || 0;
|
||
if (released === fund.releasedAmout) {
|
||
await Fund.findByIdAndUpdate(fund.id, {
|
||
$set: {
|
||
[`fundList.${i}.status`]: 2 //标记失效
|
||
}
|
||
});
|
||
return 0;
|
||
}
|
||
sum += +fund.fundList[i].amount * tokenReleaseRate;
|
||
}
|
||
return sum;
|
||
}
|
||
function awardInvite(user, fund, allInviteReward) {
|
||
const rewardInvite = allInviteReward - (fund.option && fund.option.usedGroupReward || 0); // 求增量: 总额 - 已领取总额(fundSum)
|
||
// 挖矿奖励 = 充值金额奖励 + 社区奖励
|
||
return rewardInvite;
|
||
}
|
||
// fund.option.usedGroupReward : 已领取的社群奖励总额
|
||
const doReward = async (user, fund) => {
|
||
// 1.对于充值的fundList的每一项,按照2%进行发放,累加到asset.vic,添加action,类型为1
|
||
// 2.对于社团成员进行人头数目,按照10vic/人进行奖励,累加到asset.vic,添加action,类型为2
|
||
if (!user || !fund) return 0;
|
||
const allInviteReward = (+user.group.length || 0) * 5;
|
||
const rewardMine = await awardMine(user, fund);
|
||
const rewardInvite = awardInvite(user, fund, allInviteReward);
|
||
const reward = rewardMine + rewardInvite;
|
||
|
||
reward && await Fund.findByIdAndUpdate(fund.id, {
|
||
$inc: {
|
||
'asset.vic': reward,
|
||
},
|
||
$set: {
|
||
'option.usedGroupReward': rewardInvite, // 本次的奖励值累加到已领取团队奖励总和内
|
||
'asset.usdt': allInviteReward, // 用来记录所有社群奖励总额
|
||
'lastMine': new Date().toISOString()
|
||
}
|
||
}).then(() => {
|
||
rewardMine > 0 && createAction(1, {
|
||
userId: user.id,
|
||
tokenType: 'vic',
|
||
amount: rewardMine,
|
||
remain: fund.asset
|
||
}).save();
|
||
rewardInvite > 0 && createAction(2, {
|
||
userId: user.id,
|
||
tokenType: 'vic',
|
||
amount: rewardInvite,
|
||
remain: fund.asset
|
||
}).save();
|
||
});
|
||
};
|
||
const mock = {
|
||
touch: () => {console.log('touch');},
|
||
done: () => {console.log('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();
|
||
}
|
||
|
||
// 批量更新,每次十个
|
||
const userFundList = await Fund.find().sort('_id').limit(10).skip(currentIndex);
|
||
typeof job.touch === 'function' && job.touch();
|
||
mylog.info('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('开始更新用户: ', user.id);
|
||
await doReward(user, fund);
|
||
mylog.info('用户: ', user.id, '更新完毕');
|
||
typeof job.touch === 'function' && job.touch();
|
||
await system.updateOne({ key: 'userIndex4job' }, {
|
||
$set: {
|
||
value: currentIndex
|
||
}
|
||
});
|
||
}
|
||
mylog.info('收益计算分配完成');
|
||
typeof done === 'function' && done('收益计算分配完成');
|
||
}
|
||
async function confirm (job = mock, done = mock) {
|
||
mylog.info('[Schedule Job] 矿晶确认程序开始执行......');
|
||
const unconfirmed = await Fund.find({
|
||
'fundList.status': 0
|
||
});
|
||
mylog.info('共有' + unconfirmed.length + '用户需要确认');
|
||
unconfirmed.forEach(async (fund) => {
|
||
const len = fund.fundList.length || 0;
|
||
let acc = 0;
|
||
for (let i = 0; i < len; ++i) {
|
||
const fundItem = fund.fundList[i];
|
||
if (fundItem && fundItem.status === 0) {
|
||
++acc;
|
||
await Fund.findByIdAndUpdate(fund.id, {
|
||
$set: {
|
||
[`fundList.${i}.status`]: 1 // 确认激活
|
||
}
|
||
});
|
||
}
|
||
}
|
||
mylog.info(`用户${fund.userId} 共更新${acc}条`);
|
||
job.touch();
|
||
});
|
||
return typeof done === 'function' && done('矿晶确认完成');
|
||
}
|
||
const jobs = [
|
||
{
|
||
name: 'mine',
|
||
processor: mine,
|
||
type: 'every',
|
||
slot: '0 0 3 * * *',
|
||
priority: 'highest',
|
||
unique: {
|
||
id: 'mine'
|
||
},
|
||
skipImmediate: true,
|
||
timezone: "ETC/GMT+8"
|
||
},
|
||
{
|
||
name: 'confirm',
|
||
processor: confirm,
|
||
type: 'every',
|
||
slot: '0 0/10 * * * *', // 59 */1 * * *
|
||
priority: 'highest',
|
||
unique: {
|
||
id: 'confirm'
|
||
},
|
||
// skipImmediate: true,
|
||
timezone: "ETC/GMT+8"
|
||
}
|
||
];
|
||
function mountJobs (Job) {
|
||
jobs.map(jobConfig => {
|
||
const { name, processor, type, slot, priority, unique, skipImmediate, timezone } = jobConfig;
|
||
Job.define(name, {
|
||
timezone,
|
||
skipImmediate,
|
||
priority,
|
||
unique: { 'data.type': 'active', 'data.userId': unique.id }
|
||
}, processor);
|
||
Job[type](slot, name);
|
||
});
|
||
}
|
||
|
||
module.exports = Job => mountJobs(Job); |