feat:裁剪
This commit is contained in:
180
src/jobs/jobs.js
180
src/jobs/jobs.js
@@ -16,99 +16,97 @@
|
|||||||
*/
|
*/
|
||||||
const mongoose = require('mongoose');
|
const mongoose = require('mongoose');
|
||||||
const User = require('../modules/user/user.model');
|
const User = require('../modules/user/user.model');
|
||||||
const Estate = require('../modules/estate/estate.model');
|
|
||||||
const EstateOrder = require('../modules/estateOrder/estateOrder.model');
|
|
||||||
const { createAction } = require('../modules/action/action.handler');
|
const { createAction } = require('../modules/action/action.handler');
|
||||||
const jobs = [
|
const jobs = [
|
||||||
{
|
// {
|
||||||
name: 'refreshEstateAndPayBack',
|
// name: 'refreshEstateAndPayBack',
|
||||||
processor: async (job, done) => {
|
// processor: async (job, done) => {
|
||||||
//todo: 程序内检查是否可以执行,保证不会启动时错误的自动执行一次
|
// //todo: 程序内检查是否可以执行,保证不会启动时错误的自动执行一次
|
||||||
const now = new Date().getMinutes();
|
// const now = new Date().getMinutes();
|
||||||
if (now > 0 && now < 58) {
|
// if (now > 0 && now < 58) {
|
||||||
mylog.error('[Schedule Job] 不在合理时间启动...程序结束');
|
// mylog.error('[Schedule Job] 不在合理时间启动...程序结束');
|
||||||
return 0;
|
// return 0;
|
||||||
}
|
// }
|
||||||
mylog.info('开始定时计算作业...');
|
// mylog.info('开始定时计算作业...');
|
||||||
const query = { startTime: +(new Date().getUTCHours()) + 8 + 1 };
|
// const query = { startTime: +(new Date().getUTCHours()) + 8 + 1 };
|
||||||
const elist = await Estate.find(query).exec();
|
// const elist = await Estate.find(query).exec();
|
||||||
if (!elist || elist.length <= 0) return 0;
|
// if (!elist || elist.length <= 0) return 0;
|
||||||
const [ estateSession, userSession, orderSession ] = await Promise.all([
|
// const [ estateSession, userSession, orderSession ] = await Promise.all([
|
||||||
mongoose.startSession(),
|
// mongoose.startSession(),
|
||||||
mongoose.startSession(),
|
// mongoose.startSession(),
|
||||||
mongoose.startSession()
|
// mongoose.startSession()
|
||||||
]);
|
// ]);
|
||||||
await Promise.all([
|
// await Promise.all([
|
||||||
estateSession.startTransaction(),
|
// estateSession.startTransaction(),
|
||||||
userSession.startTransaction(),
|
// userSession.startTransaction(),
|
||||||
orderSession.startTransaction()
|
// orderSession.startTransaction()
|
||||||
]);
|
// ]);
|
||||||
job.touch();
|
// job.touch();
|
||||||
for (let i = 0, length = elist.length; i < length; i++) {
|
// for (let i = 0, length = elist.length; i < length; i++) {
|
||||||
const estate = elist[i];
|
// const estate = elist[i];
|
||||||
const estateId = estate.id;
|
// const estateId = estate.id;
|
||||||
const userId = estate.currentOwner;
|
// const userId = estate.currentOwner;
|
||||||
// todo: 更精确判读是否要回收地产
|
// // todo: 更精确判读是否要回收地产
|
||||||
if (!userId) continue;
|
// if (!userId) continue;
|
||||||
const [ user, order ] = await Promise.all([
|
// const [ user, order ] = await Promise.all([
|
||||||
User.findById(userId).exec(),
|
// User.findById(userId).exec(),
|
||||||
EstateOrder.findOne({
|
// EstateOrder.findOne({
|
||||||
userId,
|
// userId,
|
||||||
estateId,
|
// estateId,
|
||||||
status: 'holding'
|
// status: 'holding'
|
||||||
}).exec()
|
// }).exec()
|
||||||
]);
|
// ]);
|
||||||
const profit = +estate.price * +estate.profit;
|
// const profit = +estate.price * +estate.profit;
|
||||||
const payback = profit + estate.price;
|
// const payback = profit + estate.price;
|
||||||
const action = createAction(3, { actor: userId, estate, amount: profit, remain: user.asset });
|
// const action = createAction(3, { actor: userId, estate, amount: profit, remain: user.asset });
|
||||||
if (!Number.isFinite(payback)) throw new Error('非法的收益数值!');
|
// if (!Number.isFinite(payback)) throw new Error('非法的收益数值!');
|
||||||
const userOpts = { returnOriginal: false, session: userSession };
|
// const userOpts = { returnOriginal: false, session: userSession };
|
||||||
const orderOpts = { returnOriginal: false, session: orderSession };
|
// const orderOpts = { returnOriginal: false, session: orderSession };
|
||||||
const estateOpts = { returnOriginal: false, session: estateSession };
|
// const estateOpts = { returnOriginal: false, session: estateSession };
|
||||||
try {
|
// try {
|
||||||
await Promise.all([
|
// await Promise.all([
|
||||||
user.increase('profit', payback, userOpts),
|
// user.increase('profit', payback, userOpts),
|
||||||
estate.sell(estateOpts),
|
// estate.sell(estateOpts),
|
||||||
order.finish(orderOpts),
|
// order.finish(orderOpts),
|
||||||
action.save()
|
// action.save()
|
||||||
]);
|
// ]);
|
||||||
await Promise.all([
|
// await Promise.all([
|
||||||
userSession.commitTransaction(),
|
// userSession.commitTransaction(),
|
||||||
orderSession.commitTransaction(),
|
// orderSession.commitTransaction(),
|
||||||
estateSession.commitTransaction()
|
// estateSession.commitTransaction()
|
||||||
]);
|
// ]);
|
||||||
} catch (error) {
|
// } catch (error) {
|
||||||
mylog.error(error);
|
// mylog.error(error);
|
||||||
await Promise.all([
|
// await Promise.all([
|
||||||
action.remove(),
|
// action.remove(),
|
||||||
userSession.abortTransaction(),
|
// userSession.abortTransaction(),
|
||||||
orderSession.abortTransaction(),
|
// orderSession.abortTransaction(),
|
||||||
estateSession.abortTransaction()
|
// estateSession.abortTransaction()
|
||||||
]);
|
// ]);
|
||||||
} finally {
|
// } finally {
|
||||||
await Promise.all([
|
// await Promise.all([
|
||||||
userSession.endSession(),
|
// userSession.endSession(),
|
||||||
orderSession.endSession(),
|
// orderSession.endSession(),
|
||||||
estateSession.endSession()
|
// estateSession.endSession()
|
||||||
]);
|
// ]);
|
||||||
}
|
// }
|
||||||
job.touch();
|
// job.touch();
|
||||||
}
|
// }
|
||||||
mylog.info('收益计算分配完成');
|
// mylog.info('收益计算分配完成');
|
||||||
done();
|
// done();
|
||||||
},
|
// },
|
||||||
type: 'every',
|
// type: 'every',
|
||||||
// 写成 '*/59 * * * *' 会出现59分执行一次,下一小时的0分再执行一次
|
// // 写成 '*/59 * * * *' 会出现59分执行一次,下一小时的0分再执行一次
|
||||||
// 写成 '*/59 * * * *' 是每59
|
// // 写成 '*/59 * * * *' 是每59
|
||||||
// 最差的解决方案是 在58分执行
|
// // 最差的解决方案是 在58分执行
|
||||||
slot: '59 */1 * * *', // 59th minute per hour
|
// slot: '59 */1 * * *', // 59th minute per hour
|
||||||
priority: 'highest',
|
// priority: 'highest',
|
||||||
unique: {
|
// unique: {
|
||||||
id: 'payback001'
|
// id: 'payback001'
|
||||||
},
|
// },
|
||||||
skipImmediate: true,
|
// skipImmediate: true,
|
||||||
timezone: "ETC/GMT+8"
|
// timezone: "ETC/GMT+8"
|
||||||
}
|
// }
|
||||||
];
|
];
|
||||||
function mountJobs (Job) {
|
function mountJobs (Job) {
|
||||||
jobs.map(jobConfig => {
|
jobs.map(jobConfig => {
|
||||||
|
|||||||
68
src/modules/fund/fund.model.js
Normal file
68
src/modules/fund/fund.model.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
const ASSETTYPES = ['gin', 'vic', 'usdtBTC', 'usdtETH', 'usdtTRX'];
|
||||||
|
|
||||||
|
const FundSchema = new mongoose.Schema({
|
||||||
|
userId: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
tokenAddress: {
|
||||||
|
type: {},
|
||||||
|
default: {
|
||||||
|
'usdtBTC': '',
|
||||||
|
'usdtETH': '',
|
||||||
|
'usdtTRX': ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// fund 为充值矿晶的一系列数值,用于计算收益
|
||||||
|
fund: [{
|
||||||
|
type: Number
|
||||||
|
}],
|
||||||
|
groupFundSum: {
|
||||||
|
type: Number
|
||||||
|
},
|
||||||
|
asset: {
|
||||||
|
type: {}
|
||||||
|
},
|
||||||
|
option: {
|
||||||
|
type: {}
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
timestamps: {
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const FundModel = mongoose.model('Fund', FundSchema);
|
||||||
|
|
||||||
|
FundModel.prototype.increase = async function (assetName, amount, option = {}) {
|
||||||
|
if (!assetName || !amount || amount <= 0 || !Number.isFinite(amount) || !ASSETTYPES.includes(assetName)) return null;
|
||||||
|
const asset = 'asset.' + assetName;
|
||||||
|
const res = await FundModel.findByIdAndUpdate(this.id, {
|
||||||
|
$inc: {
|
||||||
|
[asset]: amount
|
||||||
|
}
|
||||||
|
}, { ...option, returnOriginal: false }).exec().catch(err => null);
|
||||||
|
if (!res || !res.asset) throw new Error('用户资产修改[increase]失败!');
|
||||||
|
if (res.asset[assetName] < 0) throw new Error('Insufficient funds');
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
FundModel.prototype.decrease = async function (assetName, amount, option) {
|
||||||
|
// 传入的amount应该是大于0的正数
|
||||||
|
if (!assetName || !amount || amount <= 0 || !Number.isFinite(amount) || !ASSETTYPES.includes(assetName)) return null;
|
||||||
|
amount = 0 - amount;
|
||||||
|
const asset = 'asset.' + assetName;
|
||||||
|
const res = await FundModel.findByIdAndUpdate(this.id, {
|
||||||
|
$inc: {
|
||||||
|
[asset]: amount
|
||||||
|
}
|
||||||
|
}, { ...option, returnOriginal: false }).exec().catch(err => null);
|
||||||
|
if (!res || !res.asset) throw new Error('用户资产修改[decrease]失败!');
|
||||||
|
if (res.asset[assetName] < 0) throw new Error('Insufficient funds');
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = FundModel;
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const mongoose = require('mongoose');
|
|
||||||
const OtcAds = require('./otcAds.model');
|
|
||||||
const User = require('../user/user.model');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
publishAd: async function (req, res) {
|
|
||||||
const { userId } = res.locals.user;
|
|
||||||
const { type, totalAmount, option } = req.body;
|
|
||||||
if (!Number.isFinite(+totalAmount)) return res.json({
|
|
||||||
code: 4,
|
|
||||||
msg: 'illegal amount'
|
|
||||||
});
|
|
||||||
const user = await User.findById(userId).exec();
|
|
||||||
if (!user) return res.json({
|
|
||||||
code: 1,
|
|
||||||
msg: 'unknow user'
|
|
||||||
});
|
|
||||||
const balance = +user.asset.profit - +totalAmount;
|
|
||||||
if (!Number.isFinite(balance) || balance < 0) return res.json({
|
|
||||||
code: 2,
|
|
||||||
msg: 'insufficient fund'
|
|
||||||
});
|
|
||||||
const ads = new OtcAds({ userId, type, totalAmount: +totalAmount, restAmount: +totalAmount, option });
|
|
||||||
try {
|
|
||||||
await Promise.all([
|
|
||||||
user.decrease('profit', +totalAmount),
|
|
||||||
ads.save()
|
|
||||||
]);
|
|
||||||
res.json({
|
|
||||||
code: 0,
|
|
||||||
msg: 'success',
|
|
||||||
data: ads
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
mylog.error(error);
|
|
||||||
res.json({
|
|
||||||
code: 3,
|
|
||||||
error: JSON.stringify(error)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
revokeAd: async function (req, res) {
|
|
||||||
const { adId } = req.body;
|
|
||||||
const userId = res.locals.user ? res.locals.user.userId : '';
|
|
||||||
const [ad, user] = await Promise.all([
|
|
||||||
OtcAds.findById(adId).exec(),
|
|
||||||
User.findById(userId).exec()
|
|
||||||
]);
|
|
||||||
if (!ad || !user) return res.json({
|
|
||||||
code: 1,
|
|
||||||
msg: 'unknow user or advertisement'
|
|
||||||
});
|
|
||||||
const restAmount = +(ad.restAmount);
|
|
||||||
await ad.updateOne({ status: 'canceled' }).exec();
|
|
||||||
if (ad.type === 'sell') {
|
|
||||||
// 卖单回收冻结的币,而且只能回到profit
|
|
||||||
const session = await mongoose.startSession();
|
|
||||||
await session.startTransaction();
|
|
||||||
const option = { session };
|
|
||||||
try {
|
|
||||||
await user.increase('profit', restAmount, option);
|
|
||||||
await session.commitTransaction();
|
|
||||||
session.endSession();
|
|
||||||
} catch (error) {
|
|
||||||
await session.abortTransaction();
|
|
||||||
session.endSession();
|
|
||||||
return res.json({
|
|
||||||
code: 500,
|
|
||||||
msg: '撤销出错,用户余额未能恢复'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res.json({
|
|
||||||
code: 0,
|
|
||||||
msg: 'advertisement revoked'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getAdList: async function (req, res) {
|
|
||||||
const { pageSize, pageIndex, type, status } = req.query;
|
|
||||||
OtcAds.find({ type, status }, 'type totalAmount restAmount status orders').limit(+pageSize).skip((+pageIndex - 1) * +pageSize).then(data => {
|
|
||||||
res.json({
|
|
||||||
code: 0,
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}).catch(error => {
|
|
||||||
res.json({
|
|
||||||
code: 500,
|
|
||||||
error
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getAdDetail: async function (req, res) {
|
|
||||||
const { adId } = req.query;
|
|
||||||
OtcAds.findById(adId, 'type totalAmount restAmount status orders').then(data => {
|
|
||||||
res.json({
|
|
||||||
code: 0,
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}).catch(error => {
|
|
||||||
res.json({
|
|
||||||
code: 500,
|
|
||||||
error
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const mongoose = require('mongoose');
|
|
||||||
const OtcAdsSchema = new mongoose.Schema({
|
|
||||||
userId: {
|
|
||||||
// 所属用户的objId
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
type: {
|
|
||||||
// 订单类型, 买单'buy' 卖单'sell'
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
enum: ['buy', 'sell']
|
|
||||||
},
|
|
||||||
totalAmount: {
|
|
||||||
//挂单总额
|
|
||||||
type: Number,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
restAmount: {
|
|
||||||
//当前剩余额度
|
|
||||||
type: Number,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
status: {
|
|
||||||
// 订单状态
|
|
||||||
/**
|
|
||||||
* available: 挂单中
|
|
||||||
* locked: 锁定
|
|
||||||
* finished: 结束
|
|
||||||
* canceled: 中止/取消
|
|
||||||
*/
|
|
||||||
type: String,
|
|
||||||
default: 'available',
|
|
||||||
enum: ['available', 'locked', 'finished', 'canceled']
|
|
||||||
},
|
|
||||||
orders: {
|
|
||||||
// 涉及到的所有订单详情/快照
|
|
||||||
type: {},
|
|
||||||
default: {}
|
|
||||||
},
|
|
||||||
option: {
|
|
||||||
type: {}
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
timestamps: {
|
|
||||||
createdAt: 'createdAt',
|
|
||||||
updatedAt: 'updatedAt'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const otcAdsSchema = mongoose.model('otcads', OtcAdsSchema);
|
|
||||||
|
|
||||||
module.exports = otcAdsSchema;
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
const indexRoute = '/otcads';
|
|
||||||
const controller = require('./otcAds.controller');
|
|
||||||
const services = [
|
|
||||||
{
|
|
||||||
url: '/publish',
|
|
||||||
method: 'POST',
|
|
||||||
controller: controller.publishAd
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: '/revoke',
|
|
||||||
method: 'POST',
|
|
||||||
controller: controller.revokeAd
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: '/getAdList',
|
|
||||||
method: 'GET',
|
|
||||||
controller: controller.getAdList
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: '/getAdDetail',
|
|
||||||
method: 'GET',
|
|
||||||
controller: controller.getAdDetail
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
module.exports = services.map(service => {
|
|
||||||
// 可以在此处对要导出的服务map进行统一处理
|
|
||||||
service.url = indexRoute + service.url;
|
|
||||||
return service;
|
|
||||||
});
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const otcOrder = require('./otcOrder.model');
|
|
||||||
const { buy, sell } = require('./otcOrder.handler');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
getOtcOrderList (req, res) {
|
|
||||||
const { userId } = res.locals.user;
|
|
||||||
const { pageSize, pageIndex, type } = req.query;
|
|
||||||
otcOrder.find({ type, userId }).skip((+pageIndex - 1) * +pageSize).limit(+pageSize)
|
|
||||||
.then(list => {
|
|
||||||
res.json({
|
|
||||||
code: 0,
|
|
||||||
msg: '',
|
|
||||||
data: list
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json({
|
|
||||||
code: 1,
|
|
||||||
msg: err
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getOtcOrder (req, res) {
|
|
||||||
const { userId } = res.locals.user;
|
|
||||||
const option = req.query;
|
|
||||||
otcOrder.find({ userId, ...option }, 'detail')
|
|
||||||
.then(list => {
|
|
||||||
res.json({
|
|
||||||
code: 0,
|
|
||||||
msg: '',
|
|
||||||
data: list
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json({
|
|
||||||
code: 1,
|
|
||||||
msg: err
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async buy (req, res) {
|
|
||||||
let { adId, amount } = req.body;
|
|
||||||
amount = +amount;
|
|
||||||
const { userId } = res.locals.user;
|
|
||||||
if (!userId || !adId || !Number.isFinite(amount)) return res.json({
|
|
||||||
code: 1,
|
|
||||||
msg: 'invalid parameers'
|
|
||||||
});
|
|
||||||
const exec = await buy(userId, adId, amount);
|
|
||||||
if (exec && exec.res) {
|
|
||||||
return res.json({
|
|
||||||
code: 0,
|
|
||||||
msg: exec.msg,
|
|
||||||
data: exec.res
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return res.json({
|
|
||||||
code: exec.code,
|
|
||||||
msg: exec.msg,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async sell (req, res) {
|
|
||||||
let { adId, amount } = req.body;
|
|
||||||
amount = +amount;
|
|
||||||
const { userId } = res.locals.user;
|
|
||||||
if (!userId || !adId || !Number.isFinite(amount)) return res.json({
|
|
||||||
code: 1,
|
|
||||||
msg: 'invalid parameers'
|
|
||||||
});
|
|
||||||
const exec = await sell(userId, adId, amount);
|
|
||||||
if (exec && exec.res) {
|
|
||||||
return res.json({
|
|
||||||
code: 0,
|
|
||||||
msg: exec.msg,
|
|
||||||
data: exec.res
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return res.json({
|
|
||||||
code: exec.code,
|
|
||||||
msg: exec.msg,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const mongoose = require('mongoose');
|
|
||||||
const User = require('../user/user.model');
|
|
||||||
const OtcAds = require('../otcAds/otcAds.model');
|
|
||||||
const OtcOrder = require('../otcOrder/otcOrder.model');
|
|
||||||
const { createAction } = require('../action/action.handler');
|
|
||||||
/**
|
|
||||||
|
|
||||||
* 1. 生成对应的action
|
|
||||||
* 2. 生成订单
|
|
||||||
* 3. 冻结卖家资金
|
|
||||||
* 4.
|
|
||||||
*
|
|
||||||
* @param {String} actorId
|
|
||||||
* @param {String} adId
|
|
||||||
* @param {Number} amount
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async function otcBuy (actorId, adId, amount) {
|
|
||||||
try {
|
|
||||||
const actor = await User.findById(actorId).exec();
|
|
||||||
if (!actor) return { res: null, code: 1, msg: 'unknow user' };
|
|
||||||
const adv = await OtcAds.findById(adId).exec();
|
|
||||||
const order = new OtcOrder({ });
|
|
||||||
const action = createAction(4, { actor: actorId, adv, });
|
|
||||||
let orderSession = await mongoose.startSession();
|
|
||||||
let actorSession = await mongoose.startSession();
|
|
||||||
orderSession.startTransaction();
|
|
||||||
actorSession.startTransaction();
|
|
||||||
const actorOpts = { session: actorSession, returnOriginal: false };
|
|
||||||
const orderOpts = { session: orderSession, returnOriginal: false };
|
|
||||||
try {
|
|
||||||
await action.save();
|
|
||||||
await orderSession.commitTransaction();
|
|
||||||
await actorSession.commitTransaction();
|
|
||||||
return { res : { }, code: 0, msg: 'success' };
|
|
||||||
} catch (error) {
|
|
||||||
await orderSession.abortTransaction();
|
|
||||||
await actorSession.abortTransaction();
|
|
||||||
return { res: null, code: 3, msg: 'Insufficient funds' };
|
|
||||||
} finally {
|
|
||||||
orderSession.endSession();
|
|
||||||
actorSession.endSession();
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
return { res: null, code: 500, msg: JSON.stringify(error) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function otcSell (actorId, adId, amount) {
|
|
||||||
try {
|
|
||||||
const actor = await User.findById(actorId).exec();
|
|
||||||
if (!actor) return { res: null, code: 1, msg: 'unknow user' };
|
|
||||||
const adv = await OtcAds.findById(adId).exec();
|
|
||||||
const order = new OtcOrder({ });
|
|
||||||
const action = createAction(5, { actor: actorId, adv, });
|
|
||||||
let orderSession = await mongoose.startSession();
|
|
||||||
let actorSession = await mongoose.startSession();
|
|
||||||
orderSession.startTransaction();
|
|
||||||
actorSession.startTransaction();
|
|
||||||
const actorOpts = { session: actorSession, returnOriginal: false };
|
|
||||||
const orderOpts = { session: orderSession, returnOriginal: false };
|
|
||||||
try {
|
|
||||||
await action.save();
|
|
||||||
await orderSession.commitTransaction();
|
|
||||||
await actorSession.commitTransaction();
|
|
||||||
return { res : { }, code: 0, msg: 'success' };
|
|
||||||
} catch (error) {
|
|
||||||
await action.remove();
|
|
||||||
await orderSession.abortTransaction();
|
|
||||||
await actorSession.abortTransaction();
|
|
||||||
return { res: null, code: 3, msg: 'Insufficient funds' };
|
|
||||||
} finally {
|
|
||||||
orderSession.endSession();
|
|
||||||
actorSession.endSession();
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
return { res: null, code: 500, msg: JSON.stringify(error) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
otcBuy,
|
|
||||||
otcSell
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 卖NEW的卖家广告 --- 场外买家
|
|
||||||
* 1.在广告页面,输入想要购买的数量amount,选择支付方式,选择支付币种,点击购买,发送请求。
|
|
||||||
* 2.
|
|
||||||
* 2.1 后台生成订单otcOrder,包含上面的参数。
|
|
||||||
* 2.2 冻结卖家广告中的amount数量的资金。
|
|
||||||
* 2.3 分别通知广告主和买家
|
|
||||||
* 2.4 买家付款,点击我已付款按钮,订单状态变为 买家已付款
|
|
||||||
* 2.5 卖家收到款,点击确认收款,订单状态变为,卖家已放币。
|
|
||||||
* 2.6 订单状态变更为finished
|
|
||||||
* 2.7 广告状态变更,若该广告涉及金额已空,则改为finished,否则回到available
|
|
||||||
* 3.
|
|
||||||
*/
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const mongoose = require('mongoose');
|
|
||||||
const OtcOrderSchema = new mongoose.Schema({
|
|
||||||
type: {
|
|
||||||
// 订单类型,买单[buy]or卖单[sell]
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
adId: {
|
|
||||||
// 对应广告id
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
from: {
|
|
||||||
// 转账发起人,卖家
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
to: {
|
|
||||||
// 收款人, 买家
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
coinType: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
payMethod: {
|
|
||||||
type: String,
|
|
||||||
enum : ['alipay', 'wechatpay', 'bankcard'],
|
|
||||||
},
|
|
||||||
amount: {
|
|
||||||
type: Number,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
status: {
|
|
||||||
// 订单状态
|
|
||||||
/*
|
|
||||||
* hanging: 有效
|
|
||||||
* locked: 锁定
|
|
||||||
* finished: 结束
|
|
||||||
*/
|
|
||||||
type: String,
|
|
||||||
enum : ['alipay', 'wechatpay', 'bankcard'],
|
|
||||||
},
|
|
||||||
option: {
|
|
||||||
type: {}
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
timestamps: {
|
|
||||||
createdAt: 'createdAt',
|
|
||||||
updatedAt: 'updatedAt'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const otcOrderSchema = mongoose.model('otcOrder', OtcOrderSchema);
|
|
||||||
|
|
||||||
module.exports = otcOrderSchema;
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
const indexRoute = '/otcorder';
|
|
||||||
const controller = require('./otcOrder.controller');
|
|
||||||
const services = [
|
|
||||||
{
|
|
||||||
url: '/list',
|
|
||||||
method: 'GET',
|
|
||||||
controller: controller.getOtcOrderList
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: '/detail',
|
|
||||||
method: 'GET',
|
|
||||||
controller: controller.getOtcOrder
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: '/buy',
|
|
||||||
method: 'POST',
|
|
||||||
controller: controller.buy
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: '/sell',
|
|
||||||
method: 'POST',
|
|
||||||
controller: controller.sell
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
module.exports = services.map(service => {
|
|
||||||
// 可以在此处对要导出的服务map进行统一处理
|
|
||||||
service.url = indexRoute + service.url;
|
|
||||||
return service;
|
|
||||||
});
|
|
||||||
@@ -20,34 +20,16 @@ const UserSchema = new mongoose.Schema({
|
|||||||
inviter: {
|
inviter: {
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
gender: {
|
|
||||||
type: String
|
|
||||||
},
|
|
||||||
asset: {
|
asset: {
|
||||||
type: {},
|
type: {}
|
||||||
default: {
|
|
||||||
log: 0, //充值的log
|
|
||||||
profit: 0 // 买卖地皮赚到的
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tokenAddress: {
|
|
||||||
type: {},
|
|
||||||
default: {
|
|
||||||
'usdtBTC': '',
|
|
||||||
'usdtETH': '',
|
|
||||||
'usdtTRX': ''
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
group: [],
|
||||||
avatar: {
|
avatar: {
|
||||||
type: String
|
type: String
|
||||||
},
|
},
|
||||||
option: {
|
option: {
|
||||||
type: {}
|
type: {}
|
||||||
},
|
},
|
||||||
group: [{
|
|
||||||
type: mongoose.Schema.Types.ObjectId,
|
|
||||||
ref: 'User'
|
|
||||||
}]
|
|
||||||
}, {
|
}, {
|
||||||
timestamps: {
|
timestamps: {
|
||||||
createdAt: 'createdAt',
|
createdAt: 'createdAt',
|
||||||
|
|||||||
Reference in New Issue
Block a user