This commit is contained in:
chaosBreaking
2019-08-31 14:59:13 +08:00
commit dc5423d356
65 changed files with 3813 additions and 0 deletions
+106
View File
@@ -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,
};
+48
View File
@@ -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;
+104
View File
@@ -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()
});
}
};
+102
View File
@@ -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;
+31
View File
@@ -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;
});
+141
View File
@@ -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
});
};
})
};
+36
View File
@@ -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;
});
+5
View File
@@ -0,0 +1,5 @@
const { inject } = require('../../common/Provider');
module.exports = {
};
+22
View File
@@ -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;
+11
View File
@@ -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;
});
+89
View File
@@ -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;
},
};
+33
View File
@@ -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;
+108
View File
@@ -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
});
});
}
};
+55
View File
@@ -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;
+30
View File
@@ -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,
});
}
}
};
+100
View File
@@ -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.
*/
+56
View File
@@ -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;
+30
View File
@@ -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;
});
+204
View File
@@ -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
});
}
};
+40
View File
@@ -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)
});
});
});
},
};
+31
View File
@@ -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;
+37
View File
@@ -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;
});
+90
View File
@@ -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"
});
}
}
};
+26
View File
@@ -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;
+25
View File
@@ -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;
});
+58
View File
@@ -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)
});
});
}
};
+43
View File
@@ -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
};
+58
View File
@@ -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;
+27
View File
@@ -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;
});
+71
View File
@@ -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)
});
});
}
};
+88
View File
@@ -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;
+30
View File
@@ -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;
});