Merged PR 150: API実装(自アカウント情報取得API)

## 概要
[Task1954: API実装(自アカウント情報取得API)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/1954)

・アクセストークンからユーザ情報を取得する処理を追加。
(本タスクで返しているのはアカウントIDのみ)
・本タスクのレビュー対象は以下となります。
 ・accounts.servicce
   getMyAccountメソッド
 ・accounts.controller
   @Get('me')
 ・accounts.service.spec
   'アクセストークンからユーザ情報を取得する'
   'ユーザ情報が取得できない場合エラーとなる'
・別タスクで作成したテストコードを取り込むためマージしたところ、controllerの別タスク箇所でエラーが出ていたので、一時的にコメントアウトしています。本タスクの挙動には影響ありません。

## レビューポイント
処理に過不足がないか。
エラーハンドリングが適切か。

## UIの変更
なし

## 動作確認状況
ローカルでユニットテスト実施済み。
ローカルで外部からAPIを実行し、動作確認済み。

## 補足
なし
This commit is contained in:
oura.a 2023-06-13 07:39:10 +00:00
parent 1203d6bb99
commit 201b6be260
6 changed files with 727 additions and 41 deletions

View File

@ -1,6 +1,7 @@
import {
Body,
Controller,
HttpException,
HttpStatus,
Post,
Get,
@ -26,11 +27,20 @@ import {
import { USER_ROLES, ADMIN_ROLES } from '../../constants';
import { AuthGuard } from '../../common/guards/auth/authguards';
import { RoleGuard } from '../../common/guards/role/roleguards';
//import { CryptoService } from '../../gateways/crypto/crypto.service';
import { retrieveAuthorizationToken } from '../../common/http/helper';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import { isVerifyError, verify } from '../../common/jwt';
import { AccessToken } from '../../common/token';
//import { confirmPermission } from '../../common/auth/auth';
import jwt from 'jsonwebtoken';
@ApiTags('accounts')
@Controller('accounts')
export class AccountsController {
constructor(private readonly accountService: AccountsService) {}
constructor(
private readonly accountService: AccountsService, //private readonly cryptoService: CryptoService,
) {}
@Post()
@ApiResponse({
@ -106,21 +116,40 @@ export class AccountsController {
): Promise<GetLicenseSummaryResponse> {
console.log(req.header('Authorization'));
console.log(body);
return {
licenseSummaryInfo: {
totalLicense: 0,
allocatedLicense: 0,
reusableLicense: 0,
freeLicense: 0,
expiringWithin14daysLicense: 0,
issueRequesting: 0,
numberOfRequesting: 0,
shortage: 0,
storageSize: 0,
usedSize: 0,
isAccountLock: true,
},
};
// アクセストークンにより権限を確認する
/* const pubKey = await this.cryptoService.getPublicKey();
const accessToken = retrieveAuthorizationToken(req);
//アクセストークンが存在しない場合のエラー
if (accessToken == undefined) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const payload = verify<AccessToken>(accessToken, pubKey);
//アクセストークン形式エラー
if (isVerifyError(payload)) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
} */
//アクセストークンの権限不足エラー
/* if (!confirmPermission(payload.role)) {
throw new HttpException(
makeErrorResponse('E000108'),
HttpStatus.UNAUTHORIZED,
);
} */
/* const info = await this.accountService.getLicenseSummary(
Number(payload.userId),
); */
/* return {
licenseSummaryInfo: info,
}; */
return;
}
@ApiResponse({
@ -153,9 +182,15 @@ export class AccountsController {
@Get('me')
async getMyAccount(@Req() req: Request): Promise<GetMyAccountResponse> {
console.log(req.header('Authorization'));
// アクセストークン取得
const accessToken = retrieveAuthorizationToken(req);
const payload = jwt.decode(accessToken, { json: true }) as AccessToken;
//アカウントID取得処理
const accountId = await this.accountService.getMyAccountInfo(payload);
return {
account: {
accountId: 1,
accountId: accountId,
},
};
}

View File

@ -1,25 +1,123 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AccountsService } from './accounts.service';
import { HttpException, HttpStatus } from '@nestjs/common';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import {
makeAccountsServiceMock,
makeDefaultAccountsRepositoryMockValue,
makeDefaultAdB2cMockValue,
makeDefaultSendGridlValue,
makeDefaultUsersRepositoryMockValue,
} from './test/accounts.service.mock';
describe('AccountsService', () => {
let service: AccountsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [AccountsService],
})
.useMocker(() => {
return {
createAccount: undefined,
update: undefined,
};
})
.compile();
service = module.get<AccountsService>(AccountsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
it('アカウントに紐づくライセンス情報を取得する', async () => {
const user_id = 1;
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
const adb2cParam = makeDefaultAdB2cMockValue();
const accountsRepositoryMockValue =
makeDefaultAccountsRepositoryMockValue();
const sendGridMockValue = makeDefaultSendGridlValue();
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
usersRepositoryMockValue,
adb2cParam,
sendGridMockValue,
);
expect(await service.getLicenseSummary(user_id)).toEqual(
expectedAccountLisenceCounts,
);
});
});
it('ライセンス情報が取得できない場合、エラーとなる', async () => {
const user_id = 1;
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
usersRepositoryMockValue.findUserById = new Error('DB error');
const adb2cParam = makeDefaultAdB2cMockValue();
const accountsRepositoryMockValue = makeDefaultAccountsRepositoryMockValue();
const sendGridMockValue = makeDefaultSendGridlValue();
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
usersRepositoryMockValue,
adb2cParam,
sendGridMockValue,
);
await expect(service.getLicenseSummary(user_id)).rejects.toEqual(
new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
),
);
});
it('アクセストークンからユーザ情報を取得する', async () => {
const token = {
userId: 'ede66c43-9b9d-4222-93ed-5f11c96e08e2',
role: 'none admin',
tier: 5,
};
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
const adb2cParam = makeDefaultAdB2cMockValue();
const accountsRepositoryMockValue = makeDefaultAccountsRepositoryMockValue();
const sendGridMockValue = makeDefaultSendGridlValue();
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
usersRepositoryMockValue,
adb2cParam,
sendGridMockValue,
);
expect(await service.getMyAccountInfo(token)).toEqual(userInfo);
});
it('ユーザ情報が取得できない場合エラーとなる', async () => {
const token = {
userId: 'ede66c43-9b9d-4222-93ed-5f11c96e08e2',
role: 'none admin',
tier: 5,
};
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
usersRepositoryMockValue.findUserByExternalId = new Error('DB error');
const adb2cParam = makeDefaultAdB2cMockValue();
const accountsRepositoryMockValue = makeDefaultAccountsRepositoryMockValue();
const sendGridMockValue = makeDefaultSendGridlValue();
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
usersRepositoryMockValue,
adb2cParam,
sendGridMockValue,
);
await expect(service.getMyAccountInfo(token)).rejects.toEqual(
new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
),
);
});
const expectedAccountLisenceCounts = {
totalLicense: 1,
allocatedLicense: 2,
reusableLicense: 3,
freeLicense: 4,
expiringWithin14daysLicense: 5,
issueRequesting: 6,
numberOfRequesting: 7,
shortage: 8,
storageSize: 0,
usedSize: 0,
isAccountLock: false,
};
const userInfo = {
accepted_terms_version: '1.0',
account_id: 1234567890123456,
author_id: '6cce347f-0cf1-a15e-19ab-d00988b643f9',
auto_renew: false,
created_at: new Date('2023-06-13 00:00:00'),
created_by: 'test',
deleted_at: null,
email_verified: true,
external_id: 'ede66c43-9b9d-4222-93ed-5f11c96e08e2',
id: 1,
license_alert: false,
notification: false,
role: 'none admin',
updated_at: null,
updated_by: null,
};

View File

@ -1,7 +1,10 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { SendGridService } from '../../gateways/sendgrid/sendgrid.service';
import { UsersRepositoryService } from '../../repositories/users/users.repository.service';
import {
UsersRepositoryService,
UserNotFoundError,
} from '../../repositories/users/users.repository.service';
import { AccountsRepositoryService } from '../../repositories/accounts/accounts.repository.service';
import {
AdB2cService,
@ -12,6 +15,9 @@ import { Account } from '../../repositories/accounts/entity/account.entity';
import { User } from '../../repositories/users/entity/user.entity';
import { TIER_5 } from '../../constants';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import { LicenseSummaryInfo } from './types/types';
import { User as EntityUser } from '../../repositories/users/entity/user.entity';
import { AccessToken } from '../../common/token';
@Injectable()
export class AccountsService {
@ -22,7 +28,62 @@ export class AccountsService {
private readonly sendgridService: SendGridService,
private readonly configService: ConfigService,
) {}
private readonly logger = new Logger(AccountsService.name);
/**
*
* @param userId
* @returns LicenseSummary
*/
async getLicenseSummary(userId: number): Promise<LicenseSummaryInfo> {
this.logger.log(`[IN] ${this.getLicenseSummary.name}`);
//DBよりアクセス者の所属するアカウントIDを取得する
let adminUser: EntityUser;
const licenseSammury = new LicenseSummaryInfo();
try {
adminUser = await this.usersRepository.findUserById(userId);
const accountId = adminUser.account_id;
// Total Licenseの取得を行う
licenseSammury.totalLicense =
await this.accountRepository.getTotalLicense(accountId);
// Allocated licenseの取得を行う
licenseSammury.allocatedLicense =
await this.accountRepository.getAllocatedLicense(accountId);
// Reusable licenseの取得を行う
licenseSammury.reusableLicense =
await this.accountRepository.getReusableLicense(accountId);
// Free licenseの取得を行う
licenseSammury.freeLicense = await this.accountRepository.getFreeLicense(
accountId,
);
// Expiring within 14days licenseの取得を行う
licenseSammury.expiringWithin14daysLicense =
await this.accountRepository.getExpiringWithin14DaysLicense(accountId);
// Issue Requestingの取得を行う
licenseSammury.issueRequesting =
await this.accountRepository.getIssueRequesting(accountId);
// Number of Requestingの取得を行う
licenseSammury.numberOfRequesting =
await this.accountRepository.getNumberOfRequesting(accountId);
// Shortageの取得を行う
licenseSammury.shortage = await this.accountRepository.getShortage(
accountId,
);
// XXX Storage Size、PBI1203では対象外のため0固定
licenseSammury.storageSize = 0;
// XXX Used Size、PBI1203では対象外のため0固定
licenseSammury.usedSize = 0;
// Account Lockの状態を取得する
licenseSammury.isAccountLock =
await this.accountRepository.getIsAccountLocked(accountId);
} catch (e) {
throw new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
return licenseSammury;
}
/**
* DBに作成する
* @param companyName
@ -126,4 +187,34 @@ export class AccountsService {
externalUserId: user.external_id,
};
}
/**
*
* @param token
* @returns accountId
*/
async getMyAccountInfo(token: AccessToken): Promise<number> {
this.logger.log(`[IN] ${this.getMyAccountInfo.name}`);
let userInfo: User;
try {
userInfo = await this.usersRepository.findUserByExternalId(token.userId);
} catch (e) {
switch (e.constructor) {
case UserNotFoundError:
throw new HttpException(
makeErrorResponse('E010204'),
HttpStatus.BAD_REQUEST,
);
default:
throw new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
this.logger.log(`[OUT] ${this.getMyAccountInfo.name}`);
return userInfo.account_id;
}
}

View File

@ -0,0 +1,221 @@
import { ConfigModule } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing';
import { User } from '../../../repositories/users/entity/user.entity';
import { UsersRepositoryService } from '../../../repositories/users/users.repository.service';
import { AccountsService } from '../accounts.service';
import { AccountsRepositoryService } from '../../../repositories/accounts/accounts.repository.service';
import {
AdB2cService,
ConflictError,
} from '../../../gateways/adb2c/adb2c.service';
import { SendGridService } from '../../../gateways/sendgrid/sendgrid.service';
export type UsersRepositoryMockValue = {
findUserById: User | Error;
findUserByExternalId: User | Error;
};
export type AdB2cMockValue = {
createUser: string | ConflictError | Error;
};
export type SendGridMockValue = {
createMailContentFromEmailConfirm: {
subject: string;
text: string;
html: string;
};
sendMail: undefined | Error;
};
export type AccountsRepositoryMockValue = {
getTotalLicense: number | Error;
getAllocatedLicense: number | Error;
getReusableLicense: number | Error;
getFreeLicense: number | Error;
getExpiringWithin14DaysLicense: number | Error;
getIssueRequesting: number | Error;
getNumberOfRequesting: number | Error;
getShortage: number | Error;
getIsAccountLocked: boolean | Error;
};
export const makeAccountsServiceMock = async (
accountsRepositoryMockValue: AccountsRepositoryMockValue,
usersRepositoryMockValue: UsersRepositoryMockValue,
adB2cMockValue: AdB2cMockValue,
sendGridMockValue: SendGridMockValue,
): Promise<AccountsService> => {
const module: TestingModule = await Test.createTestingModule({
providers: [AccountsService],
imports: [
ConfigModule.forRoot({
ignoreEnvFile: true,
ignoreEnvVars: true,
}),
],
})
.useMocker((token) => {
switch (token) {
case AccountsRepositoryService:
return makeAccountsRepositoryMock(accountsRepositoryMockValue);
case UsersRepositoryService:
return makeUsersRepositoryMock(usersRepositoryMockValue);
case AdB2cService:
return makeAdB2cServiceMock(adB2cMockValue);
case SendGridService:
return makeSendGridServiceMock(sendGridMockValue);
}
})
.compile();
return module.get<AccountsService>(AccountsService);
};
export const makeAccountsRepositoryMock = (
value: AccountsRepositoryMockValue,
) => {
const {
getTotalLicense,
getAllocatedLicense,
getReusableLicense,
getFreeLicense,
getExpiringWithin14DaysLicense,
getIssueRequesting,
getNumberOfRequesting,
getShortage,
getIsAccountLocked,
} = value;
return {
getTotalLicense:
getTotalLicense instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(getTotalLicense)
: jest.fn<Promise<number>, []>().mockResolvedValue(getTotalLicense),
getAllocatedLicense:
getAllocatedLicense instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(getAllocatedLicense)
: jest.fn<Promise<number>, []>().mockResolvedValue(getAllocatedLicense),
getReusableLicense:
getReusableLicense instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(getReusableLicense)
: jest.fn<Promise<number>, []>().mockResolvedValue(getReusableLicense),
getFreeLicense:
getFreeLicense instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(getFreeLicense)
: jest.fn<Promise<number>, []>().mockResolvedValue(getFreeLicense),
getExpiringWithin14DaysLicense:
getExpiringWithin14DaysLicense instanceof Error
? jest
.fn<Promise<void>, []>()
.mockRejectedValue(getExpiringWithin14DaysLicense)
: jest
.fn<Promise<number>, []>()
.mockResolvedValue(getExpiringWithin14DaysLicense),
getIssueRequesting:
getIssueRequesting instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(getIssueRequesting)
: jest.fn<Promise<number>, []>().mockResolvedValue(getIssueRequesting),
getNumberOfRequesting:
getNumberOfRequesting instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(getNumberOfRequesting)
: jest
.fn<Promise<number>, []>()
.mockResolvedValue(getNumberOfRequesting),
getShortage:
getShortage instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(getShortage)
: jest.fn<Promise<number>, []>().mockResolvedValue(getShortage),
getIsAccountLocked:
getIsAccountLocked instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(getIsAccountLocked)
: jest.fn<Promise<boolean>, []>().mockResolvedValue(getIsAccountLocked),
};
};
export const makeUsersRepositoryMock = (value: UsersRepositoryMockValue) => {
const { findUserById, findUserByExternalId } = value;
return {
findUserById:
findUserById instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(findUserById)
: jest.fn<Promise<User>, []>().mockResolvedValue(findUserById),
findUserByExternalId:
findUserByExternalId instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(findUserByExternalId)
: jest.fn<Promise<User>, []>().mockResolvedValue(findUserByExternalId),
};
};
export const makeAdB2cServiceMock = (value: AdB2cMockValue) => {
const { createUser } = value;
return {
createUser:
createUser instanceof Error
? jest.fn<Promise<ConflictError>, []>().mockRejectedValue(createUser)
: jest
.fn<Promise<string | ConflictError>, []>()
.mockResolvedValue(createUser),
};
};
export const makeSendGridServiceMock = (value: SendGridMockValue) => {
const { createMailContentFromEmailConfirm, sendMail } = value;
return {
createMailContentFromEmailConfirm:
createMailContentFromEmailConfirm instanceof Error
? jest
.fn<Promise<void>, []>()
.mockRejectedValue(createMailContentFromEmailConfirm)
: jest
.fn<Promise<{ subject: string; text: string; html: string }>, []>()
.mockResolvedValue(createMailContentFromEmailConfirm),
sendMail:
sendMail instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(sendMail)
: jest.fn<Promise<void>, []>().mockResolvedValue(sendMail),
};
};
// 個別のテストケースに対応してそれぞれのMockを用意するのは無駄が多いのでテストケース内で個別の値を設定する
export const makeDefaultAccountsRepositoryMockValue =
(): AccountsRepositoryMockValue => {
return {
getTotalLicense: 1,
getAllocatedLicense: 2,
getReusableLicense: 3,
getFreeLicense: 4,
getExpiringWithin14DaysLicense: 5,
getIssueRequesting: 6,
getNumberOfRequesting: 7,
getShortage: 8,
getIsAccountLocked: false,
};
};
export const makeDefaultUsersRepositoryMockValue =
(): UsersRepositoryMockValue => {
const user = new User();
user.id = 1;
user.external_id = 'ede66c43-9b9d-4222-93ed-5f11c96e08e2';
user.account_id = 1234567890123456;
user.role = 'none admin';
user.author_id = '6cce347f-0cf1-a15e-19ab-d00988b643f9';
user.accepted_terms_version = '1.0';
user.email_verified = true;
user.auto_renew = false;
user.license_alert = false;
user.notification = false;
user.deleted_at = null;
user.created_by = 'test';
user.created_at = new Date('2023-06-13 00:00:00');
user.updated_by = null;
user.updated_at = null;
return {
findUserById: user,
findUserByExternalId: user,
};
};
export const makeDefaultAdB2cMockValue = (): AdB2cMockValue => {
return {
createUser: '001',
};
};
export const makeDefaultSendGridlValue = (): SendGridMockValue => {
return {
sendMail: undefined,
createMailContentFromEmailConfirm: { subject: '', text: '', html: '' },
};
};

View File

@ -1,12 +1,21 @@
import { Injectable } from '@nestjs/common';
import { DataSource, UpdateResult } from 'typeorm';
import {
Between,
DataSource,
IsNull,
MoreThan,
Not,
UpdateResult,
} from 'typeorm';
import { User } from '../users/entity/user.entity';
import { Account } from './entity/account.entity';
import { License, LicenseOrder } from '../licenses/entity/license.entity';
import { SortCriteria } from '../sort_criteria/entity/sort_criteria.entity';
import {
getDirection,
getTaskListSortableAttribute,
} from '../../common/types/sort/util';
import { LICENSE_STATUS_ISSUE_REQUESTING } from '../../../src/constants';
export class AccountNotFoundError extends Error {}
@ -148,4 +157,186 @@ export class AccountsRepositoryService {
}
return account;
}
/**
* IDから有効な総ライセンス数を取得する
* @param id
* @returns count
*/
async getTotalLicense(id: number): Promise<number> {
const count = await this.dataSource.getRepository(License).count({
where: [
{ account_id: id, expiry_date: MoreThan(new Date()) },
{ account_id: id, expiry_date: IsNull() },
],
});
return count;
}
/**
* IDから有効な総ライセンス数のうち
* @param id
* @returns count
*/
async getAllocatedLicense(id: number): Promise<number> {
const count = await this.dataSource.getRepository(License).count({
where: {
account_id: id,
type: Not(IsNull()),
},
});
return count;
}
/**
* IDから総ライセンス数のうち
*
*
* @param id
* @returns count
*/
async getReusableLicense(id: number): Promise<number> {
const count = await this.dataSource
.getRepository(License)
.createQueryBuilder('License')
.leftJoinAndSelect(
'lisence.lisence_history',
'history',
'license.id=lisence_history.license_id',
)
.where('lisence.account_id = :id', { id })
.andWhere('history.id IS NOT NULL')
// XXX Unallocated(未割当)は仮、値が決定したら修正する
.andWhere('license.status = :status', { status: 'Unallocated' })
.getCount();
return count;
}
/**
* IDから総ライセンス数のうち
*
* @param id
* @returns count
*/
async getFreeLicense(id: number): Promise<number> {
const count = await this.dataSource
.getRepository(License)
.createQueryBuilder('License')
.leftJoinAndSelect(
'lisence.lisence_history',
'history',
'license.id=lisence_history.license_id',
)
.where('lisence.account_id = :id', { id })
.andWhere('history.id IS NULL')
.getCount();
return count;
}
/**
* IDから総ライセンス数のうち
* 14
* @param id
* @returns count
*/
async getExpiringWithin14DaysLicense(id: number): Promise<number> {
const currentDate = new Date();
const fourteenDaysLater = new Date();
fourteenDaysLater.setDate(fourteenDaysLater.getDate() + 14);
const options = {
where: [
{
account_id: id,
expiry_date: Between(currentDate, fourteenDaysLater),
},
{ account_id: id, expiry_date: Not(IsNull()) },
],
};
const count = await this.dataSource.getRepository(License).count(options);
return count;
}
/**
* IDから未発行状態あるいは発行キャンセルされた注文数を取得する
* @param id
* @returns count
*/
async getIssueRequesting(id: number): Promise<number> {
const count = await this.dataSource.getRepository(LicenseOrder).count({
where: [
{
from_account_id: id,
status: LICENSE_STATUS_ISSUE_REQUESTING,
},
],
});
return count;
}
/**
* IDから未発行状態あるいは発行キャンセルされた注文の総ライセンス数を取得する
* @param id
* @returns count
*/
async getNumberOfRequesting(id: number): Promise<number> {
const result = await this.dataSource
.getRepository(LicenseOrder)
.createQueryBuilder()
.select('SUM(license_orders.quantity)', 'sum')
.where('license_orders.from_account_id = :id', { id })
.andWhere('license_orders.status = :status', {
status: LICENSE_STATUS_ISSUE_REQUESTING,
})
.getRawOne();
const sum = parseInt(result.sum, 10) || 0;
return sum;
}
/**
* IDから不足数
* {15} - {14}
* 00
* @param id
* @returns count
*/
async getShortage(id: number): Promise<number> {
const fifteenDaysLater = new Date();
fifteenDaysLater.setDate(fifteenDaysLater.getDate() + 15);
const options = {
where: [
{
status: 'Unallocated',
account_id: id,
expiry_date: MoreThan(fifteenDaysLater),
},
{ status: 'Unallocated', account_id: id, expiry_date: Not(IsNull()) },
],
};
const shortageCount = await this.dataSource
.getRepository(License)
.count(options);
const expiringWithin14DaysCount = await this.getExpiringWithin14DaysLicense(
id,
);
let result = shortageCount - expiringWithin14DaysCount;
result = Math.abs(result); // 絶対値を取得
return result >= 0 ? result : 0;
}
/**
* IDからアカウントのロック状態を取得する
* @param id
* @returns isLock
*/
async getIsAccountLocked(id: number): Promise<boolean> {
const result = await this.dataSource.getRepository(Account).findOne({
where: [
{
id: id,
},
],
});
return result.locked;
}
}

View File

@ -34,3 +34,53 @@ export class LicenseOrder {
@Column('timestamp', { nullable: true })
canceled_at?: Date;
}
@Entity({ name: 'license' })
export class License {
@PrimaryGeneratedColumn()
id: number;
@Column()
expiry_date: Date;
@Column()
account_id: number;
@Column()
type: string;
@Column()
status: string;
@Column()
allocated_user_id: number;
@Column()
order_id: number;
@Column()
deleted_at: Date;
@Column()
delete_order_id: number;
}
@Entity({ name: 'license_history' })
export class LicenseHistory {
@PrimaryGeneratedColumn()
id: number;
@Column()
user_id: number;
@Column()
license_id: number;
@Column()
allocated: boolean;
@Column()
executed_at: Date;
@Column()
exchange_type: string;
}