init
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
'use strict';
|
||||
const Action = require('./action.model');
|
||||
|
||||
/**
|
||||
* [0] 充值LOG
|
||||
* [1] 提现
|
||||
* [2] 购买地产
|
||||
* [3] 分红
|
||||
* [4] 场外买入
|
||||
* [5] 场外卖出
|
||||
*/
|
||||
function createAction (actCode, data) {
|
||||
switch (actCode) {
|
||||
case 0: return createDepositAction(data);
|
||||
case 1: return createWithdrawAction(data);
|
||||
case 2: return createPurchaseAction(data);
|
||||
case 3: return createBonusAction(data);
|
||||
case 4: return createOtcBuyAction(data);
|
||||
case 5: return createOtcSellAction(data);
|
||||
}
|
||||
}
|
||||
|
||||
function createDepositAction(data = {}) {
|
||||
// 充值要记录: 发起人,充值目标地址,充值货币类型,充值金额
|
||||
// 只创建action并返回,不保存,保存交给具体的业务在事务内执行
|
||||
const { actor, amount, tokenType, toAddress, remain } = data;
|
||||
if (!actor || !amount || !tokenType || !toAddress) return false;
|
||||
return new Action({
|
||||
actor,
|
||||
type: 0,
|
||||
op: 1,
|
||||
amount,
|
||||
remain,
|
||||
detail: { toAddress, tokenType }
|
||||
});
|
||||
}
|
||||
function createWithdrawAction(data = {}) {
|
||||
const { actor, amount, remain } = data;
|
||||
return new Action({
|
||||
actor,
|
||||
type: 1,
|
||||
op: 0,
|
||||
amount,
|
||||
remain,
|
||||
detail: {
|
||||
}
|
||||
});
|
||||
}
|
||||
function createPurchaseAction(data = {}) {
|
||||
const { actor, estate, amount, remain } = data;
|
||||
return new Action({
|
||||
actor,
|
||||
type: 2,
|
||||
op: 0,
|
||||
amount,
|
||||
remain,
|
||||
detail: {
|
||||
...estate
|
||||
}
|
||||
});
|
||||
}
|
||||
function createBonusAction(data = {}) {
|
||||
const { actor, estate, amount, remain } = data;
|
||||
return new Action({
|
||||
actor,
|
||||
type: 3,
|
||||
op: 1,
|
||||
amount,
|
||||
remain,
|
||||
detail: {
|
||||
...estate
|
||||
}
|
||||
});
|
||||
}
|
||||
function createOtcBuyAction(data = {}) {
|
||||
// 场外交易的actor是交易活动的发起者,也就是去买市场挂单的人。
|
||||
const { actor, otcOrder, adv, amount, remain } = data;
|
||||
return new Action({
|
||||
actor,
|
||||
type: 4,
|
||||
op: 1,
|
||||
amount,
|
||||
remain,
|
||||
detail: {
|
||||
order: otcOrder,
|
||||
adv
|
||||
}
|
||||
});
|
||||
}
|
||||
function createOtcSellAction(data = {}) {
|
||||
const { actor, otcOrder, amount, remain } = data;
|
||||
return new Action({
|
||||
actor,
|
||||
type: 5,
|
||||
op: 0,
|
||||
amount,
|
||||
remain,
|
||||
detail: {
|
||||
...otcOrder
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createAction,
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
'use strict';
|
||||
// 用户事务模型,记录用户的买入、获得收益、兑换等一系列财务交易操作。
|
||||
/**
|
||||
* [0] 充值LOG
|
||||
* [1] LOG提现为USDT
|
||||
* [2] 用户买入地产
|
||||
* [3] 地产收益分配
|
||||
* [4] 场外买入LOG
|
||||
* [5] 场外卖出LOG
|
||||
*/
|
||||
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
const ActionSchema = new mongoose.Schema({
|
||||
actor: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
amount: {
|
||||
type: String
|
||||
},
|
||||
remain: {
|
||||
// 操作前资产快照
|
||||
type: {}
|
||||
},
|
||||
op: {
|
||||
// 0支出
|
||||
// 1收入
|
||||
// note: 这里的支出和收入是针对平台内的财产 也就是log而言的
|
||||
type: Number,
|
||||
},
|
||||
detail: {
|
||||
type: {}
|
||||
}
|
||||
}, {
|
||||
timestamps: {
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
}
|
||||
});
|
||||
|
||||
const actionSchema = mongoose.model('Action', ActionSchema);
|
||||
|
||||
module.exports = actionSchema;
|
||||
@@ -0,0 +1,104 @@
|
||||
'use strict';
|
||||
|
||||
const Estate = require('./estate.model');
|
||||
const filter = estate => {
|
||||
// 用于返回前过滤敏感数据
|
||||
};
|
||||
let getGlobalState = (() => {
|
||||
let GlobalState = [];
|
||||
let lastUpdateAt = Date.now();
|
||||
let freq = 1000 * 60 * 5; // 5min更新一次
|
||||
let timezones = Array.from({length:24});
|
||||
const refresh = async () => {
|
||||
const jobs= timezones.map(async (v, i) => Estate.find({ startTime: i, status: 1 }).countDocuments());
|
||||
const data = await Promise.all(jobs).catch(err => {
|
||||
mylog.error('地产全局状态获取失败:', err);
|
||||
return null;
|
||||
});
|
||||
// eslint-disable-next-line require-atomic-updates
|
||||
if (data) GlobalState = data;
|
||||
lastUpdateAt = Date.now();
|
||||
};
|
||||
refresh();
|
||||
return async () => {
|
||||
if (GlobalState.length === 0 || Date.now() - lastUpdateAt >= freq) {
|
||||
await refresh();
|
||||
}
|
||||
return GlobalState;
|
||||
};
|
||||
})();
|
||||
|
||||
module.exports = {
|
||||
getEstateList: async function (req, res) {
|
||||
const { pageSize, pageIndex, timezone } = req.query;
|
||||
if (pageSize && pageIndex && timezone) {
|
||||
const list = await Estate.find({
|
||||
timezone,
|
||||
}, {
|
||||
skip: +pageSize * (+pageIndex - 1),
|
||||
limit: +pageSize
|
||||
}).exec();
|
||||
if (list && Array.isArray(list)) {
|
||||
res.json({
|
||||
code: 0,
|
||||
msg: '',
|
||||
data: list
|
||||
});
|
||||
}
|
||||
} else {
|
||||
res.json({
|
||||
code: 1,
|
||||
msg: 'Param required'
|
||||
});
|
||||
}
|
||||
},
|
||||
getEstateDetail: async function (req, res) {
|
||||
const { id } = req.query;
|
||||
if (id) {
|
||||
const estate = await Estate.findById(id).exec();
|
||||
if (estate) {
|
||||
res.json({
|
||||
code: 0,
|
||||
msg: '',
|
||||
data: estate
|
||||
});
|
||||
}
|
||||
}
|
||||
res.json({
|
||||
code: 1,
|
||||
msg: 'Cannot find estate, invalid id',
|
||||
data: null
|
||||
});
|
||||
},
|
||||
publishEstate: function (req, res) {
|
||||
const { data = {}} = req.body;
|
||||
const invalidEstate = !data || !data.name || (!data.basePrice && !data.price) || !data.startTime || !data.profit || data.profit > 1;
|
||||
if (!data.basePrice && data.price) data.basePrice = data.price;
|
||||
if (!data.price && data.basePrice) data.price = data.basePrice;
|
||||
if (invalidEstate) {
|
||||
return res.json({
|
||||
code: 1,
|
||||
msg: 'Invalid parameter'
|
||||
});
|
||||
}
|
||||
const estate = new Estate(data);
|
||||
return estate.save().then(newEstate => {
|
||||
res.json({
|
||||
code: 0,
|
||||
msg: 'success',
|
||||
data: newEstate
|
||||
});
|
||||
}).catch(err => {
|
||||
res.json({
|
||||
code: 1,
|
||||
msg: JSON.stringify(err)
|
||||
});
|
||||
});
|
||||
},
|
||||
getState: async function (req, res) {
|
||||
return res.json({
|
||||
code: 0,
|
||||
data: await getGlobalState()
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
'use strict';
|
||||
|
||||
const mongoose = require('mongoose');
|
||||
const EstateSchema = new mongoose.Schema({
|
||||
name: {
|
||||
// 地皮名
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true
|
||||
},
|
||||
type: {
|
||||
// 为edition 2保留,分为系统生成的地皮和玩家自建的地皮
|
||||
type: String,
|
||||
default: 'v1'
|
||||
},
|
||||
basePrice: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
price: {
|
||||
// 地皮的市场标价
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
profit: {
|
||||
// 收益率
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
startTime: {
|
||||
// 开始抢购时间
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
period: {
|
||||
// 换手周期,目前默认为24(hour)
|
||||
type: Number,
|
||||
default: 24,
|
||||
},
|
||||
status: {
|
||||
// 当前状态 不可买:可买 -> 0 : 1
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
desc: {
|
||||
// 地皮描述
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
image: {
|
||||
// 图片链接
|
||||
type: String
|
||||
},
|
||||
currentOwner: {
|
||||
type: String
|
||||
},
|
||||
lastTurnAt: {
|
||||
type: String
|
||||
},
|
||||
turnTimes: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
option: {
|
||||
type: {}
|
||||
}
|
||||
}, {
|
||||
timestamps: {
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
}
|
||||
});
|
||||
|
||||
const EstateModel = mongoose.model('Estate', EstateSchema);
|
||||
|
||||
EstateModel.prototype.purchase = async function (userId, option = {}) {
|
||||
return EstateModel.findByIdAndUpdate(this.id, {
|
||||
status: 0,
|
||||
currentOwner: userId,
|
||||
lastTurnAt: new Date().toISOString(),
|
||||
$inc: { turnTimes: 1 }
|
||||
}, option).exec().catch(err => {
|
||||
mylog.error(err);
|
||||
return null;
|
||||
});
|
||||
};
|
||||
|
||||
EstateModel.prototype.sell = async function (option = {}) {
|
||||
const price = +this.price * (1 + +this.profit);
|
||||
return EstateModel.findByIdAndUpdate(this.id, {
|
||||
$set: {
|
||||
status: 1,
|
||||
currentOwner: '',
|
||||
price,
|
||||
}
|
||||
}, option).exec().catch(err => {
|
||||
mylog.error(err);
|
||||
return null;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = EstateModel;
|
||||
@@ -0,0 +1,31 @@
|
||||
const indexRoute = '/estate';
|
||||
const controller = require('./estate.controller');
|
||||
|
||||
const services = [
|
||||
{
|
||||
url: '/list',
|
||||
method: 'GET',
|
||||
controller: controller.getEstateList
|
||||
},
|
||||
{
|
||||
url: '/detail',
|
||||
method: 'GET',
|
||||
controller: controller.getEstateDetail
|
||||
},
|
||||
{
|
||||
url: '/publish',
|
||||
method: 'POST',
|
||||
controller: controller.publishEstate
|
||||
},
|
||||
{
|
||||
url: '/state',
|
||||
method: 'GET',
|
||||
controller: controller.getState
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = services.map(service => {
|
||||
// 可以在此处对要导出的服务map进行统一处理
|
||||
service.url = indexRoute + service.url;
|
||||
return service;
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
'use strict';
|
||||
|
||||
const EstateOrder = require('./estateOrder.model');
|
||||
const { purchase } = require('./estateOrder.handler');
|
||||
|
||||
module.exports = {
|
||||
purchase: async function (req, res) {
|
||||
const actorId = res.locals.user.userId;
|
||||
const { estate } = req.body;
|
||||
const exec = await purchase(actorId, estate);
|
||||
if (exec.code === 0 && exec.res) {
|
||||
return res.json({
|
||||
code: 0,
|
||||
msg: 'success',
|
||||
data: exec.res
|
||||
});
|
||||
}
|
||||
return res.json({
|
||||
code: exec.code,
|
||||
msg: exec.msg
|
||||
});
|
||||
},
|
||||
getUserEstateList (req, res) {
|
||||
const { userId } = res.locals.user;
|
||||
const { pageSize, pageIndex, type } = req.query;
|
||||
// 只返回detail内容
|
||||
EstateOrder.find({ type, userId }, 'detail').skip((+pageIndex - 1) * +pageSize).limit(+pageSize)
|
||||
.then(list => {
|
||||
res.json({
|
||||
code: 0,
|
||||
msg: '',
|
||||
data: list
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
res.json({
|
||||
code: 1,
|
||||
msg: err
|
||||
});
|
||||
});
|
||||
},
|
||||
getUserEstateDetail (req, res) {
|
||||
const { userId } = res.locals.user;
|
||||
const option = req.query;
|
||||
EstateOrder.find({ userId, ...option }, 'detail')
|
||||
.then(list => {
|
||||
res.json({
|
||||
code: 0,
|
||||
msg: '',
|
||||
data: list
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
res.json({
|
||||
code: 1,
|
||||
msg: err
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
'use strict';
|
||||
const mongoose = require('mongoose');
|
||||
const User = require('../user/user.model');
|
||||
const Estate = require('../estate/estate.model');
|
||||
const EstateOrder = require('./estateOrder.model');
|
||||
const { createAction } = require('../action/action.handler');
|
||||
|
||||
module.exports = {
|
||||
purchase: async function (actorId, estateId) {
|
||||
/*
|
||||
* 1. 生成对应的action
|
||||
* 2. 将目标estate打上用户印记
|
||||
* 3. 在estateOrder内记录用户买入情况
|
||||
* 4. 对用户扣款
|
||||
*/
|
||||
try {
|
||||
const [actor, estate] = await Promise.all([
|
||||
User.findById(actorId).exec(),
|
||||
Estate.findById(estateId).exec()
|
||||
]);
|
||||
if (!actor) return { res: null, code: 1, msg: 'unknow user' };
|
||||
if (!estate || estate.status !== 1) return { res: null, code: 1, msg: 'estate unavailable' };
|
||||
const action = createAction(2, { actor: actorId, estate, amount: estate.price, remain: actor.asset });
|
||||
const order = new EstateOrder({ userId: actorId, estateId, type: '0', status: 'holding', amount: estate.price , detail: { ...estate } });
|
||||
if (!Number.isFinite(+estate.price)) throw new Error('非法的地产价格!');
|
||||
let [ estateSession, orderSession, actorSession ] = await Promise.all([
|
||||
mongoose.startSession(),
|
||||
mongoose.startSession(),
|
||||
mongoose.startSession()
|
||||
]);
|
||||
await Promise.all([
|
||||
estateSession.startTransaction(),
|
||||
orderSession.startTransaction(),
|
||||
actorSession.startTransaction()
|
||||
]);
|
||||
const estateOpts = { session: estateSession, returnOriginal: false };
|
||||
const orderOpts = { session: orderSession, returnOriginal: false };
|
||||
const actorOpts = { session: actorSession, returnOriginal: false };
|
||||
try {
|
||||
await Promise.all([
|
||||
estate.purchase(actorId, estateOpts),
|
||||
order.save(orderOpts),
|
||||
actor.decrease('log', +estate.price, actorOpts),
|
||||
action.save()
|
||||
]);
|
||||
await Promise.all([
|
||||
estateSession.commitTransaction(),
|
||||
orderSession.commitTransaction(),
|
||||
actorSession.commitTransaction()
|
||||
]);
|
||||
return { res : action.toJSON(), code: 0 };
|
||||
} catch (error) {
|
||||
// mylog.error(error);
|
||||
await Promise.all([
|
||||
action.remove(),
|
||||
estateSession.abortTransaction(),
|
||||
orderSession.abortTransaction(),
|
||||
actorSession.abortTransaction()
|
||||
]);
|
||||
return { res: null, code: 3, msg: 'Insufficient funds' };
|
||||
} finally {
|
||||
await Promise.all([
|
||||
estateSession.endSession(),
|
||||
orderSession.endSession(),
|
||||
actorSession.endSession()
|
||||
]);
|
||||
}
|
||||
} catch (error) {
|
||||
mylog.error(error);
|
||||
return { res: null, code: 500, msg: JSON.stringify(error) };
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
'use strict';
|
||||
|
||||
const mongoose = require('mongoose');
|
||||
const EstateOrderSchema = new mongoose.Schema({
|
||||
userId: {
|
||||
// 订单所属用户的objId
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
estateId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
// 订单类型
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
amount: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
status: {
|
||||
// 订单状态
|
||||
/**
|
||||
* holding: 持有
|
||||
* sold: 卖出
|
||||
*/
|
||||
type: String,
|
||||
},
|
||||
detail: {
|
||||
// 订单详情/快照
|
||||
type: {},
|
||||
required: true
|
||||
},
|
||||
option: {
|
||||
type: {}
|
||||
}
|
||||
}, {
|
||||
timestamps: {
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
}
|
||||
});
|
||||
|
||||
const EstateOrderModel = mongoose.model('estateOrder', EstateOrderSchema);
|
||||
|
||||
EstateOrderModel.prototype.finish = async function (option) {
|
||||
return this.updateOne({
|
||||
$set: {
|
||||
status: 'sold'
|
||||
}
|
||||
}, option).exec().catch(err => {
|
||||
throw new Error('订单完成状态转换失败', JSON.stringify(err));
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = EstateOrderModel;
|
||||
@@ -0,0 +1,25 @@
|
||||
const indexRoute = '/estateorder';
|
||||
const controller = require('./estateOrder.controller');
|
||||
const services = [
|
||||
{
|
||||
url: '/list',
|
||||
method: 'GET',
|
||||
controller: controller.getUserEstateList
|
||||
},
|
||||
{
|
||||
url: '/detail',
|
||||
method: 'GET',
|
||||
controller: controller.getUserEstateDetail
|
||||
},
|
||||
{
|
||||
url: '/purchase',
|
||||
method: 'POST',
|
||||
controller: controller.purchase
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = services.map(service => {
|
||||
// 可以在此处对要导出的服务map进行统一处理
|
||||
service.url = indexRoute + service.url;
|
||||
return service;
|
||||
});
|
||||
@@ -0,0 +1,141 @@
|
||||
'use strict';
|
||||
|
||||
const User = require('../user/user.model');
|
||||
const Action = require('../action/action.model');
|
||||
const { createAction } = require('../action/action.handler');
|
||||
const { createTicket } = require('../ticket/ticket.handler');
|
||||
const { deriveNewAccount } = require('../../utils/account');
|
||||
const actionFilter = 'id type amount detail op createdAt';
|
||||
const { inject } = require('../../common/Provider');
|
||||
|
||||
module.exports = {
|
||||
async getFundDetail (req, res) {
|
||||
const { userId } = res.locals.user;
|
||||
const user = await User.findById(userId, 'asset').exec().catch(err => {
|
||||
mylog.error("[getFundDetail]", err);
|
||||
return null;
|
||||
});
|
||||
if (user && user.asset) {
|
||||
return res.json({
|
||||
code: 0,
|
||||
msg: 'success',
|
||||
data: user.asset
|
||||
});
|
||||
}
|
||||
return res.json({
|
||||
code: 1,
|
||||
error: 'unknow user'
|
||||
});
|
||||
},
|
||||
async getActions (req, res) {
|
||||
const { userId } = res.locals.user;
|
||||
const { pageIndex, pageSize } = req.query;
|
||||
return await Action.find({
|
||||
actor: userId
|
||||
}, actionFilter).skip((pageIndex - 1) * pageSize).limit(pageSize)
|
||||
.exec().then(data => {
|
||||
return res.json({
|
||||
code: 0,
|
||||
data
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
return res.json({
|
||||
code: 1,
|
||||
error: JSON.stringify(err)
|
||||
});
|
||||
});
|
||||
},
|
||||
async getActionDetail (req, res) {
|
||||
const { id } = req.query;
|
||||
return await Action.findById(id, actionFilter).exec().then(data => {
|
||||
res.json({
|
||||
code: 0,
|
||||
data
|
||||
});
|
||||
}).catch(err => res.json({
|
||||
code: 1,
|
||||
error: JSON.stringify(err)
|
||||
}));
|
||||
},
|
||||
async getSupportCoinsList (req, res) {
|
||||
// eslint-disable-next-line no-undef
|
||||
const coinList = await Provider.getModule('System').getValue('supportCoins');
|
||||
return res.json({
|
||||
code: 0,
|
||||
data: coinList
|
||||
});
|
||||
},
|
||||
async withdraw (req, res) {
|
||||
/**
|
||||
* 1.创建工单
|
||||
* 2.创建用户action
|
||||
* 3.返回状态
|
||||
*/
|
||||
const { userId } = res.locals.user;
|
||||
const { amount } = req.body;
|
||||
const user = await User.findById(userId).exec();
|
||||
if (user.asset && user.asset.log < amount) return res.json({
|
||||
code: 1,
|
||||
error: 'insufficient fund'
|
||||
});
|
||||
const action = createAction(1, { actor: userId, amount, remain: user.asset, op: 0 });
|
||||
const ticket = createTicket('withdraw', { userId, amount, action });
|
||||
try {
|
||||
await user.decrease('log', +amount);
|
||||
} catch (error) {
|
||||
mylog.error('[Fund提现] ', error);
|
||||
return res.json({
|
||||
code: 2,
|
||||
msg: '扣款失败',
|
||||
error: JSON.stringify(error)
|
||||
});
|
||||
}
|
||||
Promise.all([ticket.save(), action.save()]).then(() => {
|
||||
return res.json({
|
||||
code: 0,
|
||||
msg: '工单已创建'
|
||||
});
|
||||
}).catch(err => {
|
||||
return res.json({
|
||||
code: 3,
|
||||
msg: '工单创建失败',
|
||||
error: JSON.stringify(err)
|
||||
});
|
||||
});
|
||||
},
|
||||
getDepositAddress: inject(function (SysConfig) {
|
||||
const { ROOTSECWORD } = SysConfig;
|
||||
if (!ROOTSECWORD) throw new Error('ROOTSECWORD is required');
|
||||
return async (req, res) => {
|
||||
const { userId } = res.locals.user;
|
||||
const user = await User.findById(userId).exec();
|
||||
let data = user.tokenAddress;
|
||||
if (!data.usdtBTC || !data.usdtETH) {
|
||||
const seed = parseInt(userId.substring(0, 8), 16).toString().slice(4); //前4位与Date.now前4位重复;
|
||||
const btc = deriveNewAccount(ROOTSECWORD, { coin: 'BTC', seed });
|
||||
const eth = deriveNewAccount(ROOTSECWORD, { coin: 'ETH', seed });
|
||||
const user = await User.findByIdAndUpdate(userId, {
|
||||
$set: {
|
||||
tokenAddress: {
|
||||
usdtBTC: btc,
|
||||
usdtETH: eth
|
||||
}
|
||||
}
|
||||
}, { returnOriginal: false }).exec().catch(err => {
|
||||
mylog.error(err);
|
||||
return null;
|
||||
});
|
||||
data = user && user.tokenAddress;
|
||||
}
|
||||
return data ? res.json({
|
||||
code: 0,
|
||||
data
|
||||
}) : res.json({
|
||||
code: 1,
|
||||
error: res
|
||||
});
|
||||
};
|
||||
})
|
||||
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
'use strict';
|
||||
const indexRoute = '/fund';
|
||||
const controller = require('./fund.controller');
|
||||
const services = [
|
||||
{
|
||||
url: '/detail',
|
||||
method: 'GET',
|
||||
controller: controller.getFundDetail
|
||||
},
|
||||
{
|
||||
url: '/actions',
|
||||
method: 'GET',
|
||||
controller: controller.getActions
|
||||
},
|
||||
{
|
||||
url: '/actionDetail',
|
||||
method: 'GET',
|
||||
controller: controller.getActionDetail
|
||||
},
|
||||
{
|
||||
url: '/coins',
|
||||
method: 'GET',
|
||||
controller: controller.getSupportCoinsList
|
||||
},
|
||||
{
|
||||
url: '/queryAddress',
|
||||
method: 'GET',
|
||||
controller: controller.getDepositAddress
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = services.map(service => {
|
||||
// 可以在此处对要导出的服务map进行统一处理
|
||||
service.url = indexRoute + service.url;
|
||||
return service;
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
const { inject } = require('../../common/Provider');
|
||||
|
||||
module.exports = {
|
||||
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
'use strict';
|
||||
|
||||
const mongoose = require('mongoose');
|
||||
const MarketSchema = new mongoose.Schema({
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
price: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
});
|
||||
|
||||
const marketSchema = mongoose.model('Market', MarketSchema);
|
||||
|
||||
module.exports = marketSchema;
|
||||
@@ -0,0 +1,11 @@
|
||||
const indexRoute = '/market';
|
||||
const controller = require('./market.controller');
|
||||
const services = [
|
||||
|
||||
];
|
||||
|
||||
module.exports = services.map(service => {
|
||||
// 可以在此处对要导出的服务map进行统一处理
|
||||
service.url = indexRoute + service.url;
|
||||
return service;
|
||||
});
|
||||
@@ -0,0 +1,89 @@
|
||||
'use strict';
|
||||
const Code = require('./msgCode.model');
|
||||
const Crypto = require('../../utils/crypto');
|
||||
const { inject } = require('../../common/Provider');
|
||||
const sendSMS = inject(require('../../utils/messenger').sendSms);
|
||||
const CODE_TYPE = {
|
||||
msgSignIn: -1,
|
||||
signUp: 1,
|
||||
forgetPasswd: 2,
|
||||
changePasswd: 3
|
||||
};
|
||||
/**
|
||||
* code type:
|
||||
* -1:免密登录
|
||||
* 0: 注册
|
||||
* 1: 重置密码
|
||||
* 2: 更换手机等信息
|
||||
* 3: 重置财务信息(资金密码,etc...)
|
||||
*/
|
||||
module.exports = {
|
||||
CODE_TYPE,
|
||||
async sendMsgCode (phone, type) {
|
||||
const validCodeType = CODE_TYPE[type];
|
||||
if (phone && type && validCodeType) {
|
||||
const code = Crypto.randomNumber({length: 6});
|
||||
const [ sms, save ] = await Promise.all([
|
||||
sendSMS(phone, {
|
||||
msgParam: { code },
|
||||
templateCode: 'SMS_142465215',
|
||||
signName: 'TIC钱包管家'
|
||||
}),
|
||||
this.saveCode({
|
||||
id: phone,
|
||||
code,
|
||||
type: CODE_TYPE[type]
|
||||
})
|
||||
]).catch(err => {
|
||||
mylog.error(err);
|
||||
return [null, null];
|
||||
});
|
||||
if (sms && save) return {
|
||||
code: 0,
|
||||
res: code
|
||||
};
|
||||
}
|
||||
return {
|
||||
code: 1,
|
||||
error: 'Invalid param'
|
||||
};
|
||||
},
|
||||
async saveCode (data) {
|
||||
const res = await (new Code({ ...data })).save();
|
||||
if(!res) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
async verifyCode (data = {}) {
|
||||
// think: 是直接查是否存在正确的{ id. code }组合来验证呢,还是先拿到id,再对比code正确?
|
||||
const { id, code, type } = data;
|
||||
const res = await Code.findOneAndDelete({ id, code, type }).exec();
|
||||
// Code.findOneAndDelete
|
||||
if (!res) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
async changeCode (data) {
|
||||
// code means: newCode
|
||||
const { id, code, type, option } = data;
|
||||
if (id && code && type) {
|
||||
const query = { id, type };
|
||||
const update = { code };
|
||||
const res = await Code.findOneAndUpdate(query, update, { new: true, ...option }).exec();
|
||||
if (res) return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
async deleteCode (data) {
|
||||
const { id, code, type } = data;
|
||||
if (id && code && type) {
|
||||
const res = await Code.deleteOne({ id, code, type }).exec();
|
||||
if (res) return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
const mongoose = require('mongoose');
|
||||
const CodeSchema = new mongoose.Schema({
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true
|
||||
},
|
||||
code: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true
|
||||
},
|
||||
type: {
|
||||
type:String,
|
||||
required: true,
|
||||
},
|
||||
expiresAt: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
expires: 3600
|
||||
}
|
||||
}, {
|
||||
timestamps: {
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
}
|
||||
});
|
||||
|
||||
const CodeModel = mongoose.model('MsgCode', CodeSchema);
|
||||
|
||||
module.exports = CodeModel;
|
||||
@@ -0,0 +1,108 @@
|
||||
'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
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
'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;
|
||||
@@ -0,0 +1,30 @@
|
||||
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;
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
'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,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,100 @@
|
||||
'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.
|
||||
*/
|
||||
@@ -0,0 +1,56 @@
|
||||
'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;
|
||||
@@ -0,0 +1,30 @@
|
||||
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;
|
||||
});
|
||||
@@ -0,0 +1,204 @@
|
||||
const Crypto = require('../../utils/crypto');
|
||||
const { createToken } = require('../../utils/jwt');
|
||||
const { inject } = require('../../common/Provider');
|
||||
const User = require('../user/user.model');
|
||||
const Code = require('../msgCode/msgCode.controller');
|
||||
const { createNewUser } = require('../user/user.controller');
|
||||
const CODE_TYPE = Code.CODE_TYPE;
|
||||
|
||||
const msgCodeSignIn = async function (phone, code) {
|
||||
const validCode = await Code.verifyCode({ id: phone, code, type: CODE_TYPE['msgSignIn'] });
|
||||
return validCode ? await User.findOne({
|
||||
phone,
|
||||
}).exec() : null;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
checkExist: async function (req, res) {
|
||||
// method GET
|
||||
const { phone }= req.query;
|
||||
const isExist = await User.exists({ phone });
|
||||
if (isExist) res.json({
|
||||
code: 0,
|
||||
msg: ''
|
||||
});
|
||||
else res.json({
|
||||
code: 1,
|
||||
msg: ''
|
||||
});
|
||||
},
|
||||
signIn: inject(function(SysConfig) {
|
||||
const { JWT_SECRET, PWD_HASH_SALT } = SysConfig;
|
||||
return async (req, res) => {
|
||||
let { phone, password, code } = req.body;
|
||||
let user;
|
||||
if (code) {
|
||||
user = await msgCodeSignIn(phone, code);
|
||||
} else {
|
||||
password = Crypto.hash(password, { salt: PWD_HASH_SALT });
|
||||
user = await User.findOne({
|
||||
phone,
|
||||
password
|
||||
}).exec();
|
||||
}
|
||||
if (user) {
|
||||
const token = createToken({
|
||||
userId: user.id
|
||||
}, JWT_SECRET, {});
|
||||
res.json({
|
||||
code: 0,
|
||||
msg: 'success',
|
||||
data: {
|
||||
id: user.id,
|
||||
nickname: user.nickname,
|
||||
phone: user.phone,
|
||||
email: user.email,
|
||||
token
|
||||
}
|
||||
});
|
||||
} else {
|
||||
res.json({
|
||||
code: 1,
|
||||
error: 'unknow user'
|
||||
});
|
||||
}
|
||||
};
|
||||
}),
|
||||
signUp: inject(function(SysConfig) {
|
||||
const { JWT_SECRET, PWD_HASH_SALT } = SysConfig;
|
||||
return async (req, res) => {
|
||||
let { phone, code, inviter, password } = req.body;
|
||||
if (!phone || !code || !password) return res.json({
|
||||
code: 1,
|
||||
msg: 'Missing required parameters'
|
||||
});
|
||||
const isCodeValid = await Code.verifyCode({ id: phone, code, type: CODE_TYPE['signUp'] });
|
||||
password = Crypto.hash(password, { salt: PWD_HASH_SALT });
|
||||
if (isCodeValid) {
|
||||
createNewUser({
|
||||
phone,
|
||||
inviter,
|
||||
password
|
||||
}).then(user => {
|
||||
const token = createToken({
|
||||
userId: user.id
|
||||
}, JWT_SECRET, {});
|
||||
res.json({
|
||||
code: 0,
|
||||
msg: 'success',
|
||||
data: {
|
||||
//刚注册,需要返回的应该只有token,其他的没意义
|
||||
token
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
res.json({
|
||||
code: 3,
|
||||
msg: err,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
res.json({
|
||||
code: 2,
|
||||
msg: 'Invalid code',
|
||||
});
|
||||
}
|
||||
};
|
||||
}),
|
||||
forgetPasswd: inject(function(SysConfig) {
|
||||
const { PWD_HASH_SALT } = SysConfig;
|
||||
return async (req, res) => {
|
||||
const { phone, password, code } = req.body;
|
||||
const validCode = await Code.verifyCode({
|
||||
id: phone,
|
||||
code,
|
||||
type: CODE_TYPE['forgetPasswd']
|
||||
});
|
||||
if (!validCode) {
|
||||
res.json({
|
||||
code: 1,
|
||||
msg: 'Incorrect code'
|
||||
});
|
||||
}
|
||||
const newPasswd = Crypto.hash(password, PWD_HASH_SALT);
|
||||
const query = { phone };
|
||||
const update = { password: newPasswd };
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const success = await User.findOneAndUpdate(query, update, { new: true }).exec().catch(err => null);
|
||||
if (success) {
|
||||
res.json({
|
||||
code: 0,
|
||||
msg: 'Reseted passwd'
|
||||
});
|
||||
} else {
|
||||
res.json({
|
||||
code: 500,
|
||||
msg: 'unknow error'
|
||||
});
|
||||
}
|
||||
};
|
||||
}),
|
||||
changePasswd: inject(function (SysConfig) {
|
||||
const { PWD_HASH_SALT } = SysConfig;
|
||||
return async (req, res) => {
|
||||
const { phone, password, code } = req.body;
|
||||
const validCode = await Code.verifyCode({
|
||||
id: phone,
|
||||
code,
|
||||
type: CODE_TYPE['changePasswd']
|
||||
});
|
||||
if (!validCode) {
|
||||
res.json({
|
||||
code: 1,
|
||||
msg: 'Incorrect code'
|
||||
});
|
||||
}
|
||||
const newPasswd = Crypto.hash(password, PWD_HASH_SALT);
|
||||
const query = { phone };
|
||||
const update = { password: newPasswd };
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const success = await User.findOneAndUpdate(query, update, { new: true }).exec().catch(err => null);
|
||||
if (success) {
|
||||
res.json({
|
||||
code: 0,
|
||||
msg: 'Reseted passwd'
|
||||
});
|
||||
} else {
|
||||
res.json({
|
||||
code: 500,
|
||||
msg: 'unknow error'
|
||||
});
|
||||
}
|
||||
};
|
||||
}),
|
||||
sendMsgCode: async (req, res) => {
|
||||
// 可作为通用的发送验证码接口
|
||||
let { phone, type } = req.body;
|
||||
// 注意此处的type是字面值不是数值,应该是例如'signIn'等
|
||||
const exec = await Code.sendMsgCode(phone, type);
|
||||
if (exec && exec.code === 0) {
|
||||
res.json({
|
||||
code: 0,
|
||||
msg: 'Code sended'
|
||||
});
|
||||
} else {
|
||||
res.json({
|
||||
code: exec.code ? exec.code : 1,
|
||||
error: exec.error ? exec.error : 'failed'
|
||||
});
|
||||
}
|
||||
},
|
||||
getLocaleSupport: async (req, res) => {
|
||||
const System = global.Provider.getModule('System');
|
||||
const locale = await System.getValue('locale');
|
||||
if (!locale) return res.json({
|
||||
code: 1,
|
||||
error: "does't exists"
|
||||
});
|
||||
return res.json({
|
||||
code: 0,
|
||||
data: locale
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
const indexRoute = '/sign';
|
||||
const controller = require('./sign.controller');
|
||||
const services = [
|
||||
{
|
||||
url: '/check',
|
||||
method: 'GET',
|
||||
controller: controller.checkExist
|
||||
},
|
||||
{
|
||||
url: '/in',
|
||||
method: 'POST',
|
||||
controller: controller.signIn
|
||||
},
|
||||
{
|
||||
url: '/up',
|
||||
method: 'POST',
|
||||
controller: controller.signUp
|
||||
},
|
||||
{
|
||||
url: '/forget',
|
||||
method: 'POST',
|
||||
controller: controller.forgetPasswd
|
||||
},
|
||||
{
|
||||
url: '/send',
|
||||
method: 'POST',
|
||||
controller: controller.sendMsgCode
|
||||
},
|
||||
{
|
||||
url: '/code',
|
||||
method: 'GET',
|
||||
controller: controller.getLocaleSupport
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = services.map(service => {
|
||||
// 可以在此处对要导出的服务map进行统一处理
|
||||
service.url = indexRoute + service.url;
|
||||
return service;
|
||||
});
|
||||
@@ -0,0 +1,81 @@
|
||||
"use strict";
|
||||
|
||||
const Board = require('./sysBoard.model');
|
||||
|
||||
module.exports = {
|
||||
async publish (req, res) {
|
||||
const { data = {} } = req.body;
|
||||
new Board(data).save().then(anno => {
|
||||
return res.json({
|
||||
code: 0,
|
||||
data: anno
|
||||
});
|
||||
}).catch(err => {
|
||||
mylog.error(err);
|
||||
return res.json({
|
||||
code: 1,
|
||||
error: JSON.stringify(err)
|
||||
});
|
||||
});
|
||||
},
|
||||
async getList (req, res) {
|
||||
const { config } = req.query;
|
||||
return Board.find(config, 'id title short url option createdAt').then(data => {
|
||||
return res.json({
|
||||
code: 0,
|
||||
data
|
||||
});
|
||||
}).catch(err => {
|
||||
mylog.error(err);
|
||||
return res.json({
|
||||
code: 1,
|
||||
error: JSON.stringify(err)
|
||||
});
|
||||
});
|
||||
},
|
||||
async getOne (req, res) {
|
||||
const { id } = req.query;
|
||||
return Board.findById(id, 'id title url option body author').then(data => {
|
||||
return res.json({
|
||||
code: 0,
|
||||
data
|
||||
}).catch(err => {
|
||||
mylog.error(err);
|
||||
return res.json({
|
||||
code: 1,
|
||||
error: JSON.stringify(err)
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
async updateOne (req, res) {
|
||||
const { id, data } = req.body;
|
||||
return Board.findByIdAndUpdate(id, data).then(data => {
|
||||
return res.json({
|
||||
code: 0,
|
||||
data
|
||||
}).catch(err => {
|
||||
mylog.error(err);
|
||||
return res.json({
|
||||
code: 1,
|
||||
error: JSON.stringify(err)
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
async deleteOne (req, res) {
|
||||
const { id, data } = req.body;
|
||||
return Board.findByIdAndDelete(id).then(data => {
|
||||
return res.json({
|
||||
code: 0,
|
||||
data
|
||||
}).catch(err => {
|
||||
mylog.error(err);
|
||||
return res.json({
|
||||
code: 1,
|
||||
error: JSON.stringify(err)
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
'use strict';
|
||||
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
const SysBoardSchema = new mongoose.Schema({
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
author: {
|
||||
type: String,
|
||||
},
|
||||
short: {
|
||||
type: String,
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
},
|
||||
body: {
|
||||
type: {}
|
||||
},
|
||||
option: {
|
||||
type: {}
|
||||
}
|
||||
},{
|
||||
timestamps: {}
|
||||
});
|
||||
|
||||
const SysBoardModel = mongoose.model('SysBoard', SysBoardSchema);
|
||||
|
||||
module.exports = SysBoardModel;
|
||||
@@ -0,0 +1,37 @@
|
||||
"use strict";
|
||||
|
||||
const indexRoute = '/sysboard';
|
||||
const controller = require('./sysBoard.controller');
|
||||
const services = [
|
||||
{
|
||||
url: '/publish',
|
||||
method: 'POST',
|
||||
controller: controller.publish
|
||||
},
|
||||
{
|
||||
url: '/update',
|
||||
method: 'POST',
|
||||
controller: controller.updateOne
|
||||
},
|
||||
{
|
||||
url: '/list',
|
||||
method: 'GET',
|
||||
controller: controller.getList
|
||||
},
|
||||
{
|
||||
url: '/detail',
|
||||
method: 'GET',
|
||||
controller: controller.getOne
|
||||
},
|
||||
{
|
||||
url: '/delete',
|
||||
method: 'POST',
|
||||
controller: controller.deleteOne
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = services.map(service => {
|
||||
// 可以在此处对要导出的服务map进行统一处理
|
||||
service.url = indexRoute + service.url;
|
||||
return service;
|
||||
});
|
||||
@@ -0,0 +1,90 @@
|
||||
const System = require('./system.model');
|
||||
const _cache = {};
|
||||
const provider = {
|
||||
getValue: async function (key) {
|
||||
if (!_cache[key]) {
|
||||
await System.findOne({ key }).then(data => {
|
||||
_cache[key] = data.value;
|
||||
}).catch(() => {
|
||||
mylog.error('unexpected key', key);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
return _cache[key];
|
||||
}
|
||||
};
|
||||
module.exports = {
|
||||
provider,
|
||||
getConfig: function (req, res) {
|
||||
const { key } = req.query;
|
||||
if (!_cache[key]) {
|
||||
System.findOne({ key }).then(data => {
|
||||
_cache[key] = data.value;
|
||||
res.json({
|
||||
code: 0,
|
||||
data
|
||||
});
|
||||
}).catch(err => {
|
||||
res.json({
|
||||
code: 1,
|
||||
err
|
||||
});
|
||||
});
|
||||
} else res.json({
|
||||
code: 0,
|
||||
data: _cache[key]
|
||||
});
|
||||
},
|
||||
publishConfig: async function (req, res) {
|
||||
const { key, value, option } = req.body;
|
||||
const isExist = await System.exists({ key });
|
||||
if (!isExist) {
|
||||
new System({ key, value, option }).save()
|
||||
.then(data => {
|
||||
_cache[key] = data.value;
|
||||
res.json({
|
||||
code: 0,
|
||||
msg: 'success',
|
||||
data
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
res.json({
|
||||
code: 2,
|
||||
err
|
||||
});
|
||||
});
|
||||
} else {
|
||||
res.json({
|
||||
code: 1,
|
||||
msg: 'key already existed'
|
||||
});
|
||||
}
|
||||
},
|
||||
updateConfig: async function (req, res) {
|
||||
const { key, value, option } = req.body;
|
||||
const isExist = await System.exists({ key });
|
||||
if (isExist) {
|
||||
System.findOneAndUpdate({ key }, { value, option }, { new: true })
|
||||
.then(data => {
|
||||
_cache[key] = data.value;
|
||||
res.json({
|
||||
code: 0,
|
||||
msg: 'success',
|
||||
data
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
res.json({
|
||||
code: 2,
|
||||
err
|
||||
});
|
||||
});
|
||||
} else {
|
||||
res.json({
|
||||
code: 1,
|
||||
err: "key doesn't existed"
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
const mongoose = require('mongoose');
|
||||
const SystemSchema = new mongoose.Schema({
|
||||
key: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true
|
||||
},
|
||||
value: {
|
||||
type: {},
|
||||
required: true
|
||||
},
|
||||
option: {
|
||||
type: {}
|
||||
},
|
||||
}, {
|
||||
timestamps: {
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
}
|
||||
});
|
||||
|
||||
const systemSchema = mongoose.model('System', SystemSchema);
|
||||
|
||||
module.exports = systemSchema;
|
||||
@@ -0,0 +1,25 @@
|
||||
const indexRoute = '/system';
|
||||
const controller = require('./system.controller');
|
||||
const services = [
|
||||
{
|
||||
url: '/get',
|
||||
method: 'GET',
|
||||
controller: controller.getConfig
|
||||
},
|
||||
{
|
||||
url: '/publish',
|
||||
method: 'POST',
|
||||
controller: controller.publishConfig
|
||||
},
|
||||
{
|
||||
url: '/update',
|
||||
method: 'POST',
|
||||
controller: controller.updateConfig
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = services.map(service => {
|
||||
// 可以在此处对要导出的服务map进行统一处理
|
||||
service.url = indexRoute + service.url;
|
||||
return service;
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
'use strict';
|
||||
|
||||
const Ticket = require('./ticket.model');
|
||||
const handleTicket = require('./ticket.handler');
|
||||
|
||||
module.exports = {
|
||||
async reqHandleTicket (req, res) {
|
||||
const { handler } = res.local.handler;
|
||||
const { id } = req.body;
|
||||
const ticket = await Ticket.findById(id).exec().catch(err => {
|
||||
mylog.error(err);
|
||||
return null;
|
||||
});
|
||||
if (!ticket) return res.json({
|
||||
code: 1,
|
||||
msg: 'unknow ticket'
|
||||
});
|
||||
handleTicket(ticket, handler).then(data => {
|
||||
return res.json({
|
||||
code: 0,
|
||||
msg: 'success'
|
||||
});
|
||||
}).catch(err => {
|
||||
return res.json({
|
||||
code: 1,
|
||||
error: JSON.stringify(err)
|
||||
});
|
||||
});
|
||||
},
|
||||
async getTicketList (req, res) {
|
||||
const { pageSize, pageIndex } = req.query;
|
||||
return Ticket.find().skip((pageIndex - 1) * pageSize).limit(pageSize).then(data => {
|
||||
return res.json({
|
||||
code: 0,
|
||||
data
|
||||
});
|
||||
}).catch(err => {
|
||||
return res.json({
|
||||
code: 1,
|
||||
error: JSON.stringify(err)
|
||||
});
|
||||
});
|
||||
},
|
||||
async getTicketDetail (req, res) {
|
||||
const { ticketId } = req.query;
|
||||
return Ticket.findById(ticketId).then(data => {
|
||||
return res.json({
|
||||
code: 0,
|
||||
data
|
||||
});
|
||||
}).catch(err => {
|
||||
return res.json({
|
||||
code: 1,
|
||||
error: JSON.stringify(err)
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
const Ticket = require('./ticket.model');
|
||||
const User = require('../user/user.model');
|
||||
const name2type = {
|
||||
withdraw: 0
|
||||
};
|
||||
async function handleTicket (ticket, handler) {
|
||||
// 所有工单在此处理,只要状态未完结(status !== 'finished'),就可以多次调用改变状态。
|
||||
const { userId, status } = ticket;
|
||||
const user = await User.findById(userId).exec();
|
||||
if (!user) return null;
|
||||
ticket = await ticket.next(handler);
|
||||
if (status === 'processing') {
|
||||
await dealTicket(ticket, user);
|
||||
await ticket.next(handler);
|
||||
return ticket;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function dealTicket (ticket, user) {
|
||||
if (ticket.type === 0) {
|
||||
// 提现
|
||||
dealWithdraw(ticket, user);
|
||||
}
|
||||
}
|
||||
|
||||
async function dealWithdraw (ticket, user) {
|
||||
|
||||
}
|
||||
|
||||
function createTicket (name, data) {
|
||||
const type = name2type[name];
|
||||
const status = 'created';
|
||||
const { userId, handler, action, option } = data;
|
||||
return new Ticket({
|
||||
status, type, userId, handler, action, option
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createTicket,
|
||||
handleTicket
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
'use strict';
|
||||
|
||||
const mongoose = require('mongoose');
|
||||
// 用户提现专用工单!!!
|
||||
const TicketSchema = new mongoose.Schema({
|
||||
userId: {
|
||||
// 订单所属用户的objId
|
||||
type: String,
|
||||
},
|
||||
type: {
|
||||
// 订单类型
|
||||
type: String,
|
||||
},
|
||||
amount: {
|
||||
type: Number
|
||||
},
|
||||
status: {
|
||||
type: String
|
||||
},
|
||||
action: {
|
||||
type: {}
|
||||
},
|
||||
handler: {
|
||||
type: String
|
||||
},
|
||||
option: {
|
||||
type: {}
|
||||
}
|
||||
},{
|
||||
timestamps: {}
|
||||
});
|
||||
|
||||
const TicketModel = mongoose.model('Ticket', TicketSchema);
|
||||
/**
|
||||
* status: created -> processing -> finished
|
||||
*/
|
||||
TicketModel.prototype.next = async function (handler = '') {
|
||||
// handler 表示处理人
|
||||
if (this.status === 'finished') return this;
|
||||
if (this.type === 0) {
|
||||
// deposite
|
||||
if (this.status === 'created') {
|
||||
await this.update({
|
||||
status: 'processing',
|
||||
handler
|
||||
});
|
||||
}
|
||||
if (this.status === 'processing') {
|
||||
await this.update({
|
||||
status: 'finished',
|
||||
handler
|
||||
});
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
module.exports = TicketModel;
|
||||
@@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
|
||||
const indexRoute = '/ticket';
|
||||
const controller = require('./ticket.controller');
|
||||
const services = [
|
||||
{
|
||||
url: '/handle',
|
||||
method: 'POST',
|
||||
controller: controller.reqHandleTicket
|
||||
},
|
||||
{
|
||||
url: '/list',
|
||||
method: 'GET',
|
||||
controller: controller.getTicketList
|
||||
},
|
||||
{
|
||||
url: '/detail',
|
||||
method: 'GET',
|
||||
controller: controller.getTicketDetail
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = services.map(service => {
|
||||
// 可以在此处对要导出的服务map进行统一处理
|
||||
service.url = indexRoute + service.url;
|
||||
return service;
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
const User = require('./user.model');
|
||||
const MsgCode = require('../msgCode/msgCode.controller');
|
||||
const changePwd = require('../sign/sign.controller').changePasswd;
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
module.exports = {
|
||||
changePwd,
|
||||
async createNewUser (data) {
|
||||
let { phone, inviter, password } = data;
|
||||
if (inviter) inviter = new mongoose.Types.ObjectId(inviter);
|
||||
await (new User({ phone, inviter, password })).save().then(user => user).catch(err => {
|
||||
mylog.error('创建新用户失败', err);
|
||||
return null;
|
||||
});
|
||||
},
|
||||
async getUserInfo (req, res) {
|
||||
const { userId } = res.locals.user;
|
||||
User.findById(userId, 'id nickname phone').then(data => {
|
||||
res.json({
|
||||
code: 0,
|
||||
msg: 'success',
|
||||
data
|
||||
});
|
||||
}).catch(err => {
|
||||
res.json({
|
||||
code: 1,
|
||||
error: JSON.stringify(err)
|
||||
});
|
||||
});
|
||||
},
|
||||
async changePhone (req, res) {
|
||||
const { userId } = res.locals.user;
|
||||
const { newPhone, code } = req.body;
|
||||
if (!newPhone || !code) return res.json({
|
||||
code: 1,
|
||||
error: 'Missing required param'
|
||||
});
|
||||
const isValidCode = MsgCode.verifyCode({ id: userId, code, type: 2 });
|
||||
if (!isValidCode) return res.json({
|
||||
code: 2,
|
||||
error: 'Incorrect Code'
|
||||
});
|
||||
return User.findByIdAndUpdate(userId, {
|
||||
phone: newPhone
|
||||
}).then(data => {
|
||||
res.json({
|
||||
code: 0,
|
||||
data
|
||||
});
|
||||
}).catch(err => res.json({
|
||||
code: 500,
|
||||
error: JSON.stringify(err)
|
||||
}));
|
||||
},
|
||||
async getGroup (req, res) {
|
||||
const { userId } = res.locals.user;
|
||||
User.find({ inviter: userId }).then(data => {
|
||||
res.json({
|
||||
code: 0,
|
||||
msg: 'success',
|
||||
data
|
||||
});
|
||||
}).catch(err => {
|
||||
res.json({
|
||||
code: 1,
|
||||
msg: 'failed',
|
||||
error: JSON.stringify(err)
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,88 @@
|
||||
'use strict';
|
||||
|
||||
const mongoose = require('mongoose');
|
||||
const ASSETTYPES = ['log', 'profit', 'usdtBTC', 'usdtETH', 'usdtTRX'];
|
||||
|
||||
const UserSchema = new mongoose.Schema({
|
||||
phone: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true
|
||||
},
|
||||
password: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
nickname: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
inviter: {
|
||||
type: String,
|
||||
},
|
||||
gender: {
|
||||
type: String
|
||||
},
|
||||
asset: {
|
||||
type: {},
|
||||
default: {
|
||||
log: 0, //充值的log
|
||||
profit: 0 // 买卖地皮赚到的
|
||||
}
|
||||
},
|
||||
tokenAddress: {
|
||||
type: {},
|
||||
default: {
|
||||
'usdtBTC': '',
|
||||
'usdtETH': '',
|
||||
'usdtTRX': ''
|
||||
}
|
||||
},
|
||||
avatar: {
|
||||
type: String
|
||||
},
|
||||
option: {
|
||||
type: {}
|
||||
},
|
||||
group: [{
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'User'
|
||||
}]
|
||||
}, {
|
||||
timestamps: {
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const UserModel = mongoose.model('User', UserSchema);
|
||||
|
||||
UserModel.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 UserModel.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;
|
||||
};
|
||||
UserModel.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 UserModel.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 = UserModel;
|
||||
@@ -0,0 +1,30 @@
|
||||
const indexRoute = '/user';
|
||||
const controller = require('./user.controller');
|
||||
const services = [
|
||||
{
|
||||
url: '/info',
|
||||
method: 'GET',
|
||||
controller: controller.getUserInfo
|
||||
},
|
||||
{
|
||||
url: '/chpasswd',
|
||||
method: 'POST',
|
||||
controller: controller.changePwd
|
||||
},
|
||||
{
|
||||
url: '/chphone',
|
||||
method: 'POST',
|
||||
controller: controller.changePhone
|
||||
},
|
||||
{
|
||||
url: '/group',
|
||||
method: 'GET',
|
||||
controller: controller.getGroup
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = services.map(service => {
|
||||
// 可以在此处对要导出的服务map进行统一处理
|
||||
service.url = indexRoute + service.url;
|
||||
return service;
|
||||
});
|
||||
Reference in New Issue
Block a user