@chevre/domain
Version:
Chevre Domain Library for Node.js
436 lines (435 loc) • 18.5 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AccountRepo = void 0;
const account_1 = require("./mongoose/schemas/account");
const factory = require("../factory");
const settings_1 = require("../settings");
/**
* 口座リポジトリ
*/
class AccountRepo {
constructor(connection) {
this.accountModel = connection.model(account_1.modelName, (0, account_1.createSchema)());
}
// tslint:disable-next-line:cyclomatic-complexity max-func-body-length
static CREATE_MONGO_CONDITIONS(params) {
var _a, _b, _c, _d, _e, _f, _g, _h;
const andConditions = [];
const accountTypeEq = params.accountType;
// tslint:disable-next-line:no-single-line-block-comment
/* istanbul ignore else */
if (typeof accountTypeEq === 'string') {
andConditions.push({
accountType: accountTypeEq
});
}
// tslint:disable-next-line:no-single-line-block-comment
/* istanbul ignore else */
if (params.project !== undefined && params.project !== null) {
// tslint:disable-next-line:no-single-line-block-comment
/* istanbul ignore else */
if (params.project.id !== undefined && params.project.id !== null) {
// tslint:disable-next-line:no-single-line-block-comment
/* istanbul ignore else */
if (typeof params.project.id.$eq === 'string') {
andConditions.push({
'project.id': {
$eq: params.project.id.$eq
}
});
}
// tslint:disable-next-line:no-single-line-block-comment
/* istanbul ignore else */
if (typeof params.project.id.$ne === 'string') {
andConditions.push({
'project.id': {
$ne: params.project.id.$ne
}
});
}
}
}
const accountNumberEq = (_a = params.accountNumber) === null || _a === void 0 ? void 0 : _a.$eq;
// tslint:disable-next-line:no-single-line-block-comment
/* istanbul ignore else */
if (typeof accountNumberEq === 'string') {
andConditions.push({
accountNumber: { $eq: accountNumberEq }
});
}
const accountNumberIn = (_b = params.accountNumber) === null || _b === void 0 ? void 0 : _b.$in;
// tslint:disable-next-line:no-single-line-block-comment
/* istanbul ignore else */
if (Array.isArray(accountNumberIn)) {
andConditions.push({
accountNumber: { $in: accountNumberIn }
});
}
const accountNumberRegex = (_c = params.accountNumber) === null || _c === void 0 ? void 0 : _c.$regex;
// tslint:disable-next-line:no-single-line-block-comment
/* istanbul ignore else */
if (typeof accountNumberRegex === 'string' && accountNumberRegex.length > 0) {
andConditions.push({
accountNumber: { $regex: new RegExp(accountNumberRegex) }
});
}
// tslint:disable-next-line:no-single-line-block-comment
/* istanbul ignore else */
if (Array.isArray(params.accountNumbers) && params.accountNumbers.length > 0) {
andConditions.push({
accountNumber: { $in: params.accountNumbers }
});
}
// tslint:disable-next-line:no-single-line-block-comment
/* istanbul ignore else */
// if (Array.isArray(params.statuses) && params.statuses.length > 0) {
// andConditions.push({
// status: { $in: params.statuses }
// });
// }
const nameRegex = (_d = params.name) === null || _d === void 0 ? void 0 : _d.$regex;
// tslint:disable-next-line:no-single-line-block-comment
/* istanbul ignore else */
if (typeof nameRegex === 'string' && nameRegex.length > 0) {
andConditions.push({
name: { $regex: new RegExp(nameRegex) }
});
}
// tslint:disable-next-line:no-single-line-block-comment
/* istanbul ignore if */
if (typeof params.name === 'string') {
andConditions.push({
name: new RegExp(params.name)
});
}
const openDateGte = (_e = params.openDate) === null || _e === void 0 ? void 0 : _e.$gte;
// tslint:disable-next-line:no-single-line-block-comment
/* istanbul ignore else */
if (openDateGte instanceof Date) {
andConditions.push({
openDate: { $gte: openDateGte }
});
}
const openDateLte = (_f = params.openDate) === null || _f === void 0 ? void 0 : _f.$lte;
// tslint:disable-next-line:no-single-line-block-comment
/* istanbul ignore else */
if (openDateLte instanceof Date) {
andConditions.push({
openDate: { $lte: openDateLte }
});
}
const typeOfEq = (_g = params.typeOf) === null || _g === void 0 ? void 0 : _g.$eq;
if (typeof typeOfEq === 'string') {
andConditions.push({
typeOf: { $eq: typeOfEq }
});
}
const typeOfIn = (_h = params.typeOf) === null || _h === void 0 ? void 0 : _h.$in;
if (Array.isArray(typeOfIn)) {
andConditions.push({
typeOf: { $in: typeOfIn }
});
}
return andConditions;
}
/**
* 口座を開設する
*/
open(params) {
return __awaiter(this, void 0, void 0, function* () {
if (params.length > 0) {
const accounts = params.map((p) => {
return {
project: { typeOf: factory.organizationType.Project, id: p.project.id },
typeOf: factory.accountType.Account,
accountType: p.accountType,
accountNumber: p.accountNumber,
name: p.name,
balance: p.initialBalance,
availableBalance: p.initialBalance,
pendingTransactions: [],
openDate: p.openDate
};
});
const result = yield this.accountModel.insertMany(accounts, { ordered: false, rawResult: true });
if (result.insertedCount !== accounts.length) {
throw new factory.errors.Internal('all accounts not saved');
}
// return result.ops;
return accounts;
}
else {
return [];
}
});
}
/**
* 口座を解約する
*/
// public async close(params: {
// /**
// * 口座番号
// */
// accountNumber: string;
// /**
// * 解約日時
// */
// closeDate: Date;
// }) {
// const doc = await this.accountModel.findOneAndUpdate(
// {
// accountNumber: params.accountNumber,
// pendingTransactions: { $size: 0 },
// status: factory.account.AccountStatusType.Opened
// },
// {
// closeDate: params.closeDate,
// status: factory.account.AccountStatusType.Closed
// },
// {
// new: true
// }
// )
// .exec();
// // NotFoundであれば口座状態確認
// if (doc === null) {
// const account = await this.findByAccountNumber({
// accountNumber: params.accountNumber
// });
// if (account.status === factory.account.AccountStatusType.Closed) {
// // すでに口座解約済の場合
// return;
// } else if (Array.isArray(account.pendingTransactions) && account.pendingTransactions.length > 0) {
// // 進行中取引が存在する場合の場合
// throw new factory.errors.Argument('accountNumber', 'Pending transactions exist');
// } else {
// throw new factory.errors.NotFound(this.accountModel.modelName);
// }
// }
// }
/**
* 口座番号で検索する
*/
findByAccountNumber(params) {
return __awaiter(this, void 0, void 0, function* () {
const doc = yield this.accountModel.findOne({
accountNumber: params.accountNumber
})
.exec();
if (doc === null) {
throw new factory.errors.NotFound(this.accountModel.modelName);
}
return doc.toObject();
});
}
/**
* 金額を確保する
* https://en.wikipedia.org/wiki/Authorization_hold
*/
authorizeAmount(params) {
return __awaiter(this, void 0, void 0, function* () {
const doc = yield this.accountModel.findOneAndUpdate(Object.assign({ accountNumber: params.accountNumber }, (params.force === true) ? undefined : { availableBalance: { $gte: params.amount } } // 利用可能金額確認
), {
$inc: { availableBalance: -params.amount }, // 残高を減らす
$push: { pendingTransactions: params.transaction } // 進行中取引追加
}, { new: true })
.exec();
// NotFoundであれば口座状態確認
if (doc === null) {
const account = yield this.findByAccountNumber({
accountNumber: params.accountNumber
});
// if (account.status === factory.account.AccountStatusType.Closed) {
// // 口座解約済の場合
// throw new factory.errors.Argument('accountNumber', 'Account already closed');
// } else
if (typeof account.availableBalance === 'number' && account.availableBalance < params.amount) {
// 残高不足の場合
throw new factory.errors.Argument('accountNumber', 'Insufficient balance');
}
else {
throw new factory.errors.NotFound(this.accountModel.modelName);
}
}
});
}
/**
* 取引を開始する
*/
startTransaction(params) {
return __awaiter(this, void 0, void 0, function* () {
const doc = yield this.accountModel.findOneAndUpdate({
accountNumber: params.accountNumber
// status: factory.account.AccountStatusType.Opened // 開いている口座
}, { $push: { pendingTransactions: params.transaction } })
.exec();
// NotFoundであれば口座状態確認
if (doc === null) {
// const account = await this.findByAccountNumber({
// accountNumber: params.accountNumber
// });
// if (account.status === factory.account.AccountStatusType.Closed) {
// // 口座解約済の場合
// throw new factory.errors.Argument('accountNumber', 'Account already closed');
// } else {
// throw new factory.errors.NotFound(this.accountModel.modelName);
// }
throw new factory.errors.NotFound(this.accountModel.modelName);
}
});
}
/**
* 決済処理を実行する
* 口座上で進行中の取引について、実際に金額移動処理を実行します。
*/
settleTransaction(params) {
return __awaiter(this, void 0, void 0, function* () {
// 転送元があれば残高調整
if (params.fromAccountNumber !== undefined) {
yield this.accountModel.findOneAndUpdate({
accountNumber: params.fromAccountNumber,
'pendingTransactions.id': params.transactionId
}, {
$inc: {
balance: -params.amount // 残高調整
},
$pull: { pendingTransactions: { id: params.transactionId } }
})
.exec();
}
// 転送先へがあれば入金
if (params.toAccountNumber !== undefined) {
yield this.accountModel.findOneAndUpdate({
accountNumber: params.toAccountNumber,
'pendingTransactions.id': params.transactionId
}, {
$inc: {
balance: params.amount,
availableBalance: params.amount
},
$pull: { pendingTransactions: { id: params.transactionId } }
})
.exec();
}
});
}
/**
* 取引を取り消す
* 口座上で進行中の取引を中止します。
* https://www.investopedia.com/terms/v/void-transaction.asp
*/
voidTransaction(params) {
return __awaiter(this, void 0, void 0, function* () {
// 転送元があればhold解除
if (params.fromAccountNumber !== undefined) {
yield this.accountModel.findOneAndUpdate({
accountNumber: params.fromAccountNumber,
'pendingTransactions.id': params.transactionId
}, {
$inc: {
availableBalance: params.amount // 残高調整
},
$pull: { pendingTransactions: { id: params.transactionId } }
})
.exec();
}
// 転送先へがあれば進行中取引削除
if (params.toAccountNumber !== undefined) {
yield this.accountModel.findOneAndUpdate({
accountNumber: params.toAccountNumber,
'pendingTransactions.id': params.transactionId
}, {
$pull: { pendingTransactions: { id: params.transactionId } }
})
.exec();
}
});
}
/**
* 通貨転送返金
*/
returnTransaction(params) {
return __awaiter(this, void 0, void 0, function* () {
if (params.fromAccountNumber !== undefined) {
yield this.accountModel.findOneAndUpdate({
accountNumber: params.fromAccountNumber,
'retunedTransaction.id': { $ne: params.transactionId }
}, {
$inc: {
balance: params.amount,
availableBalance: params.amount
},
$push: { retunedTransaction: { id: params.transactionId } }
})
.exec();
}
if (params.toAccountNumber !== undefined) {
yield this.accountModel.findOneAndUpdate({
accountNumber: params.toAccountNumber,
'retunedTransaction.id': { $ne: params.transactionId }
}, {
$inc: {
balance: -params.amount,
availableBalance: -params.amount
},
$push: { retunedTransaction: { id: params.transactionId } }
})
.exec();
}
});
}
count(params) {
return __awaiter(this, void 0, void 0, function* () {
const conditions = AccountRepo.CREATE_MONGO_CONDITIONS(params);
return this.accountModel.countDocuments((conditions.length > 0) ? { $and: conditions } : {})
.setOptions({ maxTimeMS: settings_1.MONGO_MAX_TIME_MS })
.exec();
});
}
/**
* 口座を検索する
*/
search(params) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const conditions = AccountRepo.CREATE_MONGO_CONDITIONS(params);
const query = this.accountModel.find((conditions.length > 0) ? { $and: conditions } : {}, {
__v: 0,
createdAt: 0,
updatedAt: 0,
pendingTransactions: 0
});
// tslint:disable-next-line:no-single-line-block-comment
/* istanbul ignore else */
if (typeof params.limit === 'number' && params.limit > 0) {
const page = (typeof params.page === 'number' && params.page > 0) ? params.page : 1;
query.limit(params.limit)
.skip(params.limit * (page - 1));
}
// tslint:disable-next-line:no-single-line-block-comment
/* istanbul ignore else */
if (((_a = params.sort) === null || _a === void 0 ? void 0 : _a.openDate) !== undefined) {
query.sort({ openDate: params.sort.openDate });
}
return query.setOptions({ maxTimeMS: settings_1.MONGO_MAX_TIME_MS })
.exec()
.then((docs) => docs.map((doc) => doc.toObject()));
});
}
unsetUnnecessaryFields(params) {
return __awaiter(this, void 0, void 0, function* () {
return this.accountModel.updateMany(params.filter, { $unset: params.$unset }, { timestamps: false })
.exec();
});
}
}
exports.AccountRepo = AccountRepo;