UNPKG

@chevre/domain

Version:

Chevre Domain Library for Node.js

436 lines (435 loc) 18.5 kB
"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;