## 概要 [Task3288: セレクトのクエリに追跡用のIDと実行日時の情報を追加する](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3288) - リポジトリ内でのDB操作でSelect文となる部分にコメント(追跡ID_日時)を追加しました。 - `find`, `fineOne`, `count`を対象にしています。 - コメントを追加するにあたってContextをリポジトリメソッドの引数に追加しています。 ## レビューポイント - 対応箇所の漏れはないでしょうか? - コメントのつけ方は適切でしょうか? ## UIの変更 - なし ## 動作確認状況 - ローカルで確認
2248 lines
67 KiB
TypeScript
2248 lines
67 KiB
TypeScript
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 { AccountsRepositoryService } from '../../repositories/accounts/accounts.repository.service';
|
||
import {
|
||
AdB2cService,
|
||
ConflictError,
|
||
isConflictError,
|
||
} from '../../gateways/adb2c/adb2c.service';
|
||
import { Account } from '../../repositories/accounts/entity/account.entity';
|
||
import { User } from '../../repositories/users/entity/user.entity';
|
||
import {
|
||
TIERS,
|
||
USER_ROLES,
|
||
ADB2C_SIGN_IN_TYPE,
|
||
OPTION_ITEM_VALUE_TYPE,
|
||
MANUAL_RECOVERY_REQUIRED,
|
||
} from '../../constants';
|
||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||
import {
|
||
TypistGroup,
|
||
GetPartnerLicensesResponse,
|
||
PartnerLicenseInfo,
|
||
GetOrderHistoriesResponse,
|
||
LicenseOrder,
|
||
GetDealersResponse,
|
||
Dealer,
|
||
GetMyAccountResponse,
|
||
GetTypistGroupResponse,
|
||
GetWorktypesResponse,
|
||
GetOptionItemsResponse,
|
||
GetPartnersResponse,
|
||
PostWorktypeOptionItem,
|
||
Author,
|
||
Partner,
|
||
GetCompanyNameResponse,
|
||
} from './types/types';
|
||
import {
|
||
DateWithZeroTime,
|
||
ExpirationThresholdDate,
|
||
} from '../licenses/types/types';
|
||
import { GetLicenseSummaryResponse, Typist } from './types/types';
|
||
import { UserNotFoundError } from '../../repositories/users/errors/types';
|
||
import { UserGroupsRepositoryService } from '../../repositories/user_groups/user_groups.repository.service';
|
||
import { makePassword } from '../../common/password';
|
||
import { LicensesRepositoryService } from '../../repositories/licenses/licenses.repository.service';
|
||
import {
|
||
AccountNotFoundError,
|
||
AdminUserNotFoundError,
|
||
DealerAccountNotFoundError,
|
||
} from '../../repositories/accounts/errors/types';
|
||
import { Context } from '../../common/log';
|
||
import {
|
||
LicensesShortageError,
|
||
AlreadyIssuedError,
|
||
OrderNotFoundError,
|
||
AlreadyLicenseStatusChangedError,
|
||
AlreadyLicenseAllocatedError,
|
||
CancellationPeriodExpiredError,
|
||
} from '../../repositories/licenses/errors/types';
|
||
import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service';
|
||
import {
|
||
TypistGroupNotExistError,
|
||
TypistIdInvalidError,
|
||
} from '../../repositories/user_groups/errors/types';
|
||
import { WorktypesRepositoryService } from '../../repositories/worktypes/worktypes.repository.service';
|
||
import {
|
||
WorktypeIdAlreadyExistsError,
|
||
WorktypeIdInUseError,
|
||
WorktypeIdMaxCountError,
|
||
WorktypeIdNotFoundError,
|
||
} from '../../repositories/worktypes/errors/types';
|
||
|
||
@Injectable()
|
||
export class AccountsService {
|
||
private readonly mailFrom =
|
||
this.configService.getOrThrow<string>('MAIL_FROM');
|
||
constructor(
|
||
private readonly accountRepository: AccountsRepositoryService,
|
||
private readonly licensesRepository: LicensesRepositoryService,
|
||
private readonly usersRepository: UsersRepositoryService,
|
||
private readonly userGroupsRepository: UserGroupsRepositoryService,
|
||
private readonly worktypesRepository: WorktypesRepositoryService,
|
||
private readonly adB2cService: AdB2cService,
|
||
private readonly sendgridService: SendGridService,
|
||
private readonly blobStorageService: BlobstorageService,
|
||
private readonly configService: ConfigService,
|
||
) {}
|
||
private readonly logger = new Logger(AccountsService.name);
|
||
/**
|
||
* 第五階層用のライセンス情報を取得する
|
||
* @param accountId
|
||
* @returns LicenseSummary
|
||
*/
|
||
async getLicenseSummary(
|
||
context: Context,
|
||
accountId: number,
|
||
): Promise<GetLicenseSummaryResponse> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.getLicenseSummary.name
|
||
} | params: { ` + `accountId: ${accountId}, };`,
|
||
);
|
||
|
||
try {
|
||
const currentDate = new DateWithZeroTime();
|
||
const expiringSoonDate = new ExpirationThresholdDate(
|
||
currentDate.getTime(),
|
||
);
|
||
|
||
const { licenseSummary, isStorageAvailable } =
|
||
await this.accountRepository.getLicenseSummaryInfo(
|
||
context,
|
||
accountId,
|
||
currentDate,
|
||
expiringSoonDate,
|
||
);
|
||
|
||
const {
|
||
allocatableLicenseWithMargin,
|
||
expiringSoonLicense,
|
||
totalLicense,
|
||
allocatedLicense,
|
||
reusableLicense,
|
||
freeLicense,
|
||
issueRequesting,
|
||
numberOfRequesting,
|
||
} = licenseSummary;
|
||
|
||
let shortage = allocatableLicenseWithMargin - expiringSoonLicense;
|
||
shortage = shortage >= 0 ? 0 : Math.abs(shortage);
|
||
|
||
const licenseSummaryResponse: GetLicenseSummaryResponse = {
|
||
totalLicense,
|
||
allocatedLicense,
|
||
reusableLicense,
|
||
freeLicense,
|
||
expiringWithin14daysLicense: expiringSoonLicense,
|
||
issueRequesting,
|
||
numberOfRequesting,
|
||
storageSize: 0, // XXX PBI1201対象外
|
||
usedSize: 0, // XXX PBI1201対象外
|
||
shortage,
|
||
isStorageAvailable,
|
||
};
|
||
return licenseSummaryResponse;
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
this.logger.error(
|
||
`[${context.getTrackingId()}] get licenseSummary failed`,
|
||
);
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.getLicenseSummary.name}`,
|
||
);
|
||
}
|
||
}
|
||
/**
|
||
* アカウント情報をDBに作成する
|
||
* @param companyName
|
||
* @param country
|
||
* @param [dealerAccountId]
|
||
* @returns account
|
||
*/
|
||
async createAccount(
|
||
context: Context,
|
||
companyName: string,
|
||
country: string,
|
||
dealerAccountId: number | undefined,
|
||
email: string,
|
||
password: string,
|
||
username: string,
|
||
role: string,
|
||
acceptedEulaVersion: string,
|
||
acceptedPrivacyNoticeVersion: string,
|
||
acceptedDpaVersion: string,
|
||
): Promise<{ accountId: number; userId: number; externalUserId: string }> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.createAccount.name
|
||
} | params: { ` +
|
||
`dealerAccountId: ${dealerAccountId}, ` +
|
||
`role: ${role}, ` +
|
||
`acceptedEulaVersion: ${acceptedEulaVersion}, ` +
|
||
`acceptedPrivacyNoticeVersion: ${acceptedPrivacyNoticeVersion}, ` +
|
||
`acceptedDpaVersion: ${acceptedDpaVersion} };`,
|
||
);
|
||
try {
|
||
let externalUser: { sub: string } | ConflictError;
|
||
try {
|
||
// idpにユーザーを作成
|
||
externalUser = await this.adB2cService.createUser(
|
||
context,
|
||
email,
|
||
password,
|
||
username,
|
||
);
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
this.logger.error(
|
||
`[${context.getTrackingId()}] create externalUser failed`,
|
||
);
|
||
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
|
||
// メールアドレス重複エラー
|
||
if (isConflictError(externalUser)) {
|
||
this.logger.error(
|
||
`[${context.getTrackingId()}] email conflict. externalUser: ${externalUser}`,
|
||
);
|
||
throw new HttpException(
|
||
makeErrorResponse('E010301'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
}
|
||
|
||
let account: Account;
|
||
let user: User;
|
||
try {
|
||
// アカウントと管理者をセットで作成
|
||
const { newAccount, adminUser } =
|
||
await this.accountRepository.createAccount(
|
||
companyName,
|
||
country,
|
||
dealerAccountId,
|
||
TIERS.TIER5,
|
||
externalUser.sub,
|
||
role,
|
||
acceptedEulaVersion,
|
||
acceptedPrivacyNoticeVersion,
|
||
acceptedDpaVersion,
|
||
);
|
||
account = newAccount;
|
||
user = adminUser;
|
||
this.logger.log(
|
||
`[${context.getTrackingId()}] adminUser.external_id: ${
|
||
user.external_id
|
||
}`,
|
||
);
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
this.logger.error(`[${context.getTrackingId()}] create account failed`);
|
||
//リカバリ処理
|
||
// idpのユーザーを削除
|
||
await this.deleteAdB2cUser(externalUser.sub, context);
|
||
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
|
||
// 新規作成アカウント用のBlobコンテナを作成
|
||
try {
|
||
await this.blobStorageService.createContainer(
|
||
context,
|
||
account.id,
|
||
country,
|
||
);
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
this.logger.error(
|
||
`[${context.getTrackingId()}] create container failed`,
|
||
);
|
||
//リカバリ処理
|
||
// idpのユーザーを削除
|
||
await this.deleteAdB2cUser(externalUser.sub, context);
|
||
|
||
// DBのアカウントを削除
|
||
await this.deleteAccount(account.id, user.id, context);
|
||
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
|
||
try {
|
||
// メールの内容を構成
|
||
const { subject, text, html } =
|
||
await this.sendgridService.createMailContentFromEmailConfirm(
|
||
context,
|
||
account.id,
|
||
user.id,
|
||
email,
|
||
);
|
||
|
||
// メールを送信
|
||
await this.sendgridService.sendMail(
|
||
context,
|
||
email,
|
||
this.mailFrom,
|
||
subject,
|
||
text,
|
||
html,
|
||
);
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
this.logger.error(`[${context.getTrackingId()}] send E-mail failed`);
|
||
//リカバリ処理
|
||
// idpのユーザーを削除
|
||
await this.deleteAdB2cUser(externalUser.sub, context);
|
||
|
||
// DBのアカウントを削除
|
||
await this.deleteAccount(account.id, user.id, context);
|
||
|
||
// Blobコンテナを削除
|
||
await this.deleteBlobContainer(account.id, country, context);
|
||
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
|
||
return {
|
||
accountId: account.id,
|
||
userId: user.id,
|
||
externalUserId: user.external_id,
|
||
};
|
||
} catch (e) {
|
||
throw e;
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.createAccount.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
// AdB2cのユーザーを削除
|
||
// TODO「タスク 2452: リトライ処理を入れる箇所を検討し、実装する」の候補
|
||
private async deleteAdB2cUser(
|
||
externalUserId: string,
|
||
context: Context,
|
||
): Promise<void> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.createAccount.name
|
||
} | params: { ` + `externalUserId: ${externalUserId}};`,
|
||
);
|
||
try {
|
||
await this.adB2cService.deleteUser(externalUserId, context);
|
||
this.logger.log(
|
||
`[${context.getTrackingId()}] delete externalUser: ${externalUserId} | params: { ` +
|
||
`externalUserId: ${externalUserId}, };`,
|
||
);
|
||
} catch (error) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${error}`);
|
||
this.logger.error(
|
||
`${MANUAL_RECOVERY_REQUIRED} [${context.getTrackingId()}] Failed to delete externalUser: ${externalUserId}`,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.deleteAdB2cUser.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
// DBのアカウントを削除
|
||
private async deleteAccount(
|
||
accountId: number,
|
||
userId: number,
|
||
context: Context,
|
||
): Promise<void> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.deleteAccount.name
|
||
} | params: { accountId: ${accountId}, userId: ${userId} };`,
|
||
);
|
||
try {
|
||
await this.accountRepository.deleteAccount(accountId, userId);
|
||
this.logger.log(
|
||
`[${context.getTrackingId()}] delete account: ${accountId}, user: ${userId}`,
|
||
);
|
||
} catch (error) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${error}`);
|
||
this.logger.error(
|
||
`${MANUAL_RECOVERY_REQUIRED} [${context.getTrackingId()}] Failed to delete account: ${accountId}, user: ${userId}`,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.deleteAccount.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
// Blobコンテナを削除
|
||
// TODO「タスク 2452: リトライ処理を入れる箇所を検討し、実装する」の候補
|
||
private async deleteBlobContainer(
|
||
accountId: number,
|
||
country: string,
|
||
context: Context,
|
||
): Promise<void> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.deleteBlobContainer.name
|
||
} | params: { accountId: ${accountId} };`,
|
||
);
|
||
try {
|
||
await this.blobStorageService.deleteContainer(
|
||
context,
|
||
accountId,
|
||
country,
|
||
);
|
||
this.logger.log(
|
||
`[${context.getTrackingId()}] delete container: ${accountId}, country: ${country}`,
|
||
);
|
||
} catch (error) {
|
||
this.logger.error(
|
||
`${MANUAL_RECOVERY_REQUIRED} [${context.getTrackingId()}] Failed to delete container: ${accountId}, country: ${country}`,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.deleteBlobContainer.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* パラメータのユーザIDからアカウント情報を取得する
|
||
* @param externalId
|
||
* @returns GetMyAccountResponse
|
||
*/
|
||
async getAccountInfo(
|
||
context: Context,
|
||
externalId: string,
|
||
): Promise<GetMyAccountResponse> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.getAccountInfo.name
|
||
} | params: { ` + `externalId: ${externalId}, };`,
|
||
);
|
||
try {
|
||
let userInfo: User;
|
||
userInfo = await this.usersRepository.findUserByExternalId(
|
||
context,
|
||
externalId,
|
||
);
|
||
|
||
let accountInfo: Account;
|
||
accountInfo = await this.accountRepository.findAccountById(
|
||
context,
|
||
userInfo.account_id,
|
||
);
|
||
|
||
let parentInfo: Account | undefined;
|
||
if (accountInfo.parent_account_id) {
|
||
parentInfo = await this.accountRepository.findAccountById(
|
||
context,
|
||
accountInfo.parent_account_id,
|
||
);
|
||
}
|
||
|
||
return {
|
||
account: {
|
||
accountId: userInfo.account_id,
|
||
companyName: accountInfo.company_name,
|
||
tier: accountInfo.tier,
|
||
country: accountInfo.country,
|
||
parentAccountId: accountInfo.parent_account_id ?? undefined,
|
||
delegationPermission: accountInfo.delegation_permission,
|
||
primaryAdminUserId: accountInfo.primary_admin_user_id ?? undefined,
|
||
secondryAdminUserId: accountInfo.secondary_admin_user_id ?? undefined,
|
||
parentAccountName: parentInfo ? parentInfo.company_name : undefined,
|
||
},
|
||
};
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
switch (e.constructor) {
|
||
case UserNotFoundError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010204'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
case AccountNotFoundError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010501'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
default:
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.getAccountInfo.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
async getTypistGroups(
|
||
context: Context,
|
||
externalId: string,
|
||
): Promise<TypistGroup[]> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.getTypistGroups.name
|
||
} | params: { externalId: ${externalId} };`,
|
||
);
|
||
|
||
// TypistGroup取得
|
||
try {
|
||
const user = await this.usersRepository.findUserByExternalId(
|
||
context,
|
||
externalId,
|
||
);
|
||
const userGroups = await this.userGroupsRepository.getUserGroups(
|
||
context,
|
||
user.account_id,
|
||
);
|
||
|
||
return userGroups.map((x) => ({ id: x.id, name: x.name }));
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.getTypistGroups.name}`,
|
||
);
|
||
}
|
||
}
|
||
/**
|
||
* IDを指定してタイピストグループを取得する
|
||
* @param context
|
||
* @param externalId
|
||
* @param typistGroupId
|
||
* @returns typist group
|
||
*/
|
||
async getTypistGroup(
|
||
context: Context,
|
||
externalId: string,
|
||
typistGroupId: number,
|
||
): Promise<GetTypistGroupResponse> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.getTypistGroup.name
|
||
} | params: { externalId: ${externalId}, typistGroupId: ${typistGroupId} };`,
|
||
);
|
||
|
||
try {
|
||
const { account_id } = await this.usersRepository.findUserByExternalId(
|
||
context,
|
||
externalId,
|
||
);
|
||
const { name, userGroupMembers } =
|
||
await this.userGroupsRepository.getTypistGroup(
|
||
context,
|
||
account_id,
|
||
typistGroupId,
|
||
);
|
||
if (!userGroupMembers) {
|
||
throw new TypistGroupNotExistError(
|
||
`Typist Group is not exist. typistGroupId: ${typistGroupId}`,
|
||
);
|
||
}
|
||
|
||
return {
|
||
typistGroupName: name,
|
||
typistIds: userGroupMembers.map((x) => x.user_id),
|
||
};
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
if (e instanceof Error) {
|
||
switch (e.constructor) {
|
||
case TypistGroupNotExistError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010908'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
default:
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.getTypistGroup.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Gets typists
|
||
* @param externalId
|
||
* @returns typists
|
||
*/
|
||
async getTypists(context: Context, externalId: string): Promise<Typist[]> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.getTypists.name
|
||
} | params: { externalId: ${externalId} };`,
|
||
);
|
||
|
||
// Typist取得
|
||
try {
|
||
const typistUsers = await this.usersRepository.findTypistUsers(
|
||
context,
|
||
externalId,
|
||
);
|
||
const externalIds = typistUsers.map((x) => x.external_id);
|
||
|
||
// B2Cからユーザー名を取得する
|
||
const adb2cUsers = await this.adB2cService.getUsers(context, externalIds);
|
||
|
||
const typists = typistUsers.map((x) => {
|
||
const user = adb2cUsers.find((adb2c) => adb2c.id === x.external_id);
|
||
if (!user) {
|
||
throw new Error(
|
||
`user not found. externalId: ${x.external_id}, userId: ${x.id}`,
|
||
);
|
||
}
|
||
return {
|
||
id: x.id,
|
||
name: user.displayName,
|
||
};
|
||
});
|
||
|
||
return typists;
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.getTypists.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* アカウント内のAuthorを取得する
|
||
* @param context
|
||
* @param externalId
|
||
* @returns authors
|
||
*/
|
||
async getAuthors(context: Context, externalId: string): Promise<Author[]> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.getAuthors.name
|
||
} | params: { externalId: ${externalId} };`,
|
||
);
|
||
|
||
try {
|
||
const { account } = await this.usersRepository.findUserByExternalId(
|
||
context,
|
||
externalId,
|
||
);
|
||
|
||
if (!account) {
|
||
throw new AccountNotFoundError(
|
||
`account not found. externalId: ${externalId}`,
|
||
);
|
||
}
|
||
|
||
const authorUsers = await this.usersRepository.findAuthorUsers(
|
||
context,
|
||
account.id,
|
||
);
|
||
|
||
const authors = authorUsers.map((x) => {
|
||
if (!x.author_id) {
|
||
throw new Error(
|
||
`author_id is Not Found. externalId: ${x.external_id}, userId: ${x.id}`,
|
||
);
|
||
}
|
||
return {
|
||
id: x.id,
|
||
authorId: x.author_id,
|
||
};
|
||
});
|
||
return authors;
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
if (e instanceof Error) {
|
||
switch (e.constructor) {
|
||
case AccountNotFoundError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010501'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
default:
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.getAuthors.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* パートナーを追加する
|
||
* @param companyName パートナーの会社名
|
||
* @param country パートナーの所属する国
|
||
* @param email パートナーの管理者のメールアドレス
|
||
* @param adminName パートナーの管理者の名前
|
||
* @param creatorUserId パートナーを作成する上位階層アカウントのユーザーID
|
||
* @param creatorAccountTier パートナーを作成する上位階層アカウントの階層
|
||
*/
|
||
async createPartnerAccount(
|
||
context: Context,
|
||
companyName: string,
|
||
country: string,
|
||
email: string,
|
||
adminName: string,
|
||
creatorUserId: string,
|
||
creatorAccountTier: number,
|
||
): Promise<{ accountId: number }> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.createPartnerAccount.name
|
||
} | params: { creatorUserId: ${creatorUserId}, creatorAccountTier: ${creatorAccountTier} };`,
|
||
);
|
||
|
||
try {
|
||
let myAccountId: number;
|
||
|
||
try {
|
||
// アクセストークンからユーザーIDを取得する
|
||
myAccountId = (
|
||
await this.usersRepository.findUserByExternalId(
|
||
context,
|
||
creatorUserId,
|
||
)
|
||
).account_id;
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
if (e instanceof UserNotFoundError) {
|
||
throw new HttpException(
|
||
makeErrorResponse('E010204'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
} else {
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
|
||
const ramdomPassword = makePassword();
|
||
|
||
let externalUser: { sub: string } | ConflictError;
|
||
|
||
try {
|
||
// 管理者ユーザを作成し、AzureADB2C IDを取得する
|
||
externalUser = await this.adB2cService.createUser(
|
||
context,
|
||
email,
|
||
ramdomPassword,
|
||
adminName,
|
||
);
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
// メールアドレスが重複していた場合はエラーを返す
|
||
if (isConflictError(externalUser)) {
|
||
throw new HttpException(
|
||
makeErrorResponse('E010301'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
}
|
||
|
||
let account: Account;
|
||
let user: User;
|
||
try {
|
||
// アカウントと管理者をセットで作成
|
||
const { newAccount, adminUser } =
|
||
await this.accountRepository.createAccount(
|
||
companyName,
|
||
country,
|
||
myAccountId,
|
||
creatorAccountTier + 1,
|
||
externalUser.sub,
|
||
USER_ROLES.NONE,
|
||
undefined,
|
||
undefined,
|
||
);
|
||
account = newAccount;
|
||
user = adminUser;
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
this.logger.error(
|
||
`[${context.getTrackingId()}] create partner account failed`,
|
||
);
|
||
//リカバリ処理
|
||
// idpのユーザーを削除
|
||
await this.deleteAdB2cUser(externalUser.sub, context);
|
||
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
|
||
try {
|
||
// 新規作成アカウント用のBlobコンテナを作成
|
||
await this.blobStorageService.createContainer(
|
||
context,
|
||
account.id,
|
||
country,
|
||
);
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
this.logger.error(
|
||
`[${context.getTrackingId()}] create partner container failed`,
|
||
);
|
||
//リカバリ処理
|
||
// idpのユーザーを削除
|
||
await this.deleteAdB2cUser(externalUser.sub, context);
|
||
|
||
// DBのアカウントとユーザーを削除
|
||
await this.deleteAccount(account.id, user.id, context);
|
||
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
|
||
try {
|
||
const { subject, text, html } =
|
||
await this.sendgridService.createMailContentFromEmailConfirmForNormalUser(
|
||
context,
|
||
account.id,
|
||
user.id,
|
||
email,
|
||
);
|
||
await this.sendgridService.sendMail(
|
||
context,
|
||
email,
|
||
this.mailFrom,
|
||
subject,
|
||
text,
|
||
html,
|
||
);
|
||
return { accountId: account.id };
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
this.logger.error(
|
||
`[${context.getTrackingId()}] create partner account send mail failed`,
|
||
);
|
||
//リカバリ処理
|
||
// idpのユーザーを削除
|
||
await this.deleteAdB2cUser(externalUser.sub, context);
|
||
|
||
// DBのアカウントを削除
|
||
await this.deleteAccount(account.id, user.id, context);
|
||
|
||
// Blobコンテナを削除
|
||
await this.deleteBlobContainer(account.id, country, context);
|
||
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
} catch (e) {
|
||
throw e;
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.createPartnerAccount.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* パートナーライセンス情報を取得する
|
||
* @param limit
|
||
* @param offset
|
||
* @param accountId
|
||
* @returns getPartnerLicensesResponse
|
||
*/
|
||
async getPartnerLicenses(
|
||
context: Context,
|
||
limit: number,
|
||
offset: number,
|
||
accountId: number,
|
||
): Promise<GetPartnerLicensesResponse> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.getPartnerLicenses.name
|
||
} | params: { limit: ${limit}, offset: ${offset}, accountId: ${accountId} };`,
|
||
);
|
||
|
||
try {
|
||
const currentDate = new DateWithZeroTime();
|
||
// 第五階層のshortage算出に使用する日付情報
|
||
// 「有効期限が現在日付からしきい値以内のライセンス数」を取得するため、しきい値となる日付を作成する
|
||
const expiringSoonDate = new ExpirationThresholdDate(
|
||
currentDate.getTime(),
|
||
);
|
||
|
||
const getPartnerLicenseResult =
|
||
await this.accountRepository.getPartnerLicense(
|
||
context,
|
||
accountId,
|
||
currentDate,
|
||
expiringSoonDate,
|
||
offset,
|
||
limit,
|
||
);
|
||
|
||
// 自アカウントのShortageを算出してreturn用の変数にマージする
|
||
let ownShortage =
|
||
getPartnerLicenseResult.ownPartnerLicenseFromRepository.stockLicense -
|
||
getPartnerLicenseResult.ownPartnerLicenseFromRepository.issuedRequested;
|
||
// 「不足している値」を取得するため、負数の場合は絶対値とし、0以上の場合は0とする
|
||
ownShortage = ownShortage >= 0 ? 0 : Math.abs(ownShortage);
|
||
|
||
// return用の型にリポジトリから取得した型をマージし、不足項目(shortage)を設定する
|
||
const ownPartnerLicense: PartnerLicenseInfo = Object.assign(
|
||
{},
|
||
getPartnerLicenseResult.ownPartnerLicenseFromRepository,
|
||
{
|
||
shortage: ownShortage,
|
||
},
|
||
);
|
||
|
||
// 各子アカウントのShortageを算出してreturn用の変数にマージする
|
||
const childrenPartnerLicenses: PartnerLicenseInfo[] = [];
|
||
for (const childPartnerLicenseFromRepository of getPartnerLicenseResult.childPartnerLicensesFromRepository) {
|
||
const { allocatableLicenseWithMargin, expiringSoonLicense } =
|
||
childPartnerLicenseFromRepository;
|
||
let childShortage: number = 0;
|
||
if (childPartnerLicenseFromRepository.tier === TIERS.TIER5) {
|
||
if (
|
||
allocatableLicenseWithMargin === undefined ||
|
||
expiringSoonLicense === undefined
|
||
) {
|
||
throw new Error(
|
||
`Tier5 account has no allocatableLicenseWithMargin or expiringSoonLicense. accountId: ${accountId}`,
|
||
);
|
||
}
|
||
childShortage = allocatableLicenseWithMargin - expiringSoonLicense;
|
||
} else {
|
||
childShortage =
|
||
childPartnerLicenseFromRepository.stockLicense -
|
||
childPartnerLicenseFromRepository.issuedRequested;
|
||
}
|
||
// 「不足している値」を取得するため、負数の場合は絶対値とし、0以上の場合は0とする
|
||
childShortage = childShortage >= 0 ? 0 : Math.abs(childShortage);
|
||
|
||
const childPartnerLicense: PartnerLicenseInfo = Object.assign(
|
||
{},
|
||
childPartnerLicenseFromRepository,
|
||
{
|
||
shortage: childShortage,
|
||
},
|
||
);
|
||
|
||
childrenPartnerLicenses.push(childPartnerLicense);
|
||
}
|
||
|
||
const getPartnerLicensesResponse: GetPartnerLicensesResponse = {
|
||
total: getPartnerLicenseResult.total,
|
||
ownPartnerLicense: ownPartnerLicense,
|
||
childrenPartnerLicenses: childrenPartnerLicenses,
|
||
};
|
||
|
||
return getPartnerLicensesResponse;
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.getPartnerLicenses.name}`,
|
||
);
|
||
}
|
||
}
|
||
/**
|
||
* 注文履歴情報を取得する
|
||
* @param limit
|
||
* @param offset
|
||
* @param accountId
|
||
* @returns getOrderHistoriesResponse
|
||
*/
|
||
async getOrderHistories(
|
||
context: Context,
|
||
limit: number,
|
||
offset: number,
|
||
accountId: number,
|
||
): Promise<GetOrderHistoriesResponse> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.getOrderHistories.name
|
||
} | params: { limit: ${limit}, offset: ${offset}, accountId: ${accountId} };`,
|
||
);
|
||
|
||
try {
|
||
const licenseHistoryInfo =
|
||
await this.licensesRepository.getLicenseOrderHistoryInfo(
|
||
context,
|
||
accountId,
|
||
offset,
|
||
limit,
|
||
);
|
||
|
||
// 戻り値用に配列の詰めなおしを行う
|
||
const orderHistories: LicenseOrder[] = [];
|
||
for (const licenseOrder of licenseHistoryInfo.licenseOrders) {
|
||
const returnLicenseOrder: LicenseOrder = {
|
||
issueDate:
|
||
licenseOrder.issued_at !== null
|
||
? new Date(licenseOrder.issued_at)
|
||
.toISOString()
|
||
.substring(0, 10)
|
||
.replace(/-/g, '/')
|
||
: undefined,
|
||
numberOfOrder: licenseOrder.quantity,
|
||
orderDate: new Date(licenseOrder.ordered_at)
|
||
.toISOString()
|
||
.substring(0, 10)
|
||
.replace(/-/g, '/'),
|
||
poNumber: licenseOrder.po_number,
|
||
status: licenseOrder.status,
|
||
};
|
||
orderHistories.push(returnLicenseOrder);
|
||
}
|
||
const getOrderHistoriesResponse: GetOrderHistoriesResponse = {
|
||
total: licenseHistoryInfo.total,
|
||
orderHistories: orderHistories,
|
||
};
|
||
return getOrderHistoriesResponse;
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.getOrderHistories.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 対象の注文を発行する
|
||
* @param context
|
||
* @param orderedAccountId
|
||
* @param userId
|
||
* @param tier
|
||
* @param poNumber
|
||
*/
|
||
async issueLicense(
|
||
context: Context,
|
||
orderedAccountId: number,
|
||
userId: string,
|
||
tier: number,
|
||
poNumber: string,
|
||
): Promise<void> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.issueLicense.name
|
||
} | params: { ` +
|
||
`orderedAccountId: ${orderedAccountId}, ` +
|
||
`userId: ${userId}, ` +
|
||
`tier: ${tier}, ` +
|
||
`poNumber: ${poNumber} };`,
|
||
);
|
||
try {
|
||
// アクセストークンからユーザーIDを取得する
|
||
const myAccountId = (
|
||
await this.usersRepository.findUserByExternalId(context, userId)
|
||
).account_id;
|
||
await this.licensesRepository.issueLicense(
|
||
context,
|
||
orderedAccountId,
|
||
myAccountId,
|
||
tier,
|
||
poNumber,
|
||
);
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
if (e instanceof Error) {
|
||
switch (e.constructor) {
|
||
case OrderNotFoundError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010801'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
case AlreadyIssuedError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010803'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
case LicensesShortageError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010804'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
default:
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.issueLicense.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
// dealersのアカウント情報を取得する
|
||
async getDealers(context: Context): Promise<GetDealersResponse> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${this.getDealers.name}`,
|
||
);
|
||
|
||
try {
|
||
const dealerAccounts = await this.accountRepository.findDealerAccounts(
|
||
context,
|
||
);
|
||
|
||
const dealers: GetDealersResponse = {
|
||
dealers: dealerAccounts.map((dealerAccount): Dealer => {
|
||
return {
|
||
id: dealerAccount.id,
|
||
name: dealerAccount.company_name,
|
||
country: dealerAccount.country,
|
||
};
|
||
}),
|
||
};
|
||
|
||
return dealers;
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.getDealers.name}`,
|
||
);
|
||
}
|
||
}
|
||
/**
|
||
* タイピストグループを作成する
|
||
* @param context
|
||
* @param externalId
|
||
* @param typistGroupName
|
||
* @param typistIds
|
||
* @returns createTypistGroupResponse
|
||
**/
|
||
async createTypistGroup(
|
||
context: Context,
|
||
externalId: string,
|
||
typistGroupName: string,
|
||
typistIds: number[],
|
||
): Promise<void> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.createTypistGroup.name
|
||
} | params: { ` +
|
||
`externalId: ${externalId}, ` +
|
||
`typistGroupName: ${typistGroupName}, ` +
|
||
`typistIds: ${typistIds} };`,
|
||
);
|
||
try {
|
||
// 外部IDをもとにユーザー情報を取得する
|
||
const { account_id } = await this.usersRepository.findUserByExternalId(
|
||
context,
|
||
externalId,
|
||
);
|
||
// API実行ユーザーのアカウントIDでタイピストグループを作成し、タイピストグループとtypistIdsのユーザーを紐付ける
|
||
await this.userGroupsRepository.createTypistGroup(
|
||
context,
|
||
typistGroupName,
|
||
typistIds,
|
||
account_id,
|
||
);
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
if (e instanceof Error) {
|
||
switch (e.constructor) {
|
||
case TypistIdInvalidError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010204'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
default:
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.createTypistGroup.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* タイピストグループを更新する
|
||
* @param context
|
||
* @param externalId
|
||
* @param typistGroupId
|
||
* @param typistGroupName
|
||
* @param typistIds
|
||
* @returns typist group
|
||
*/
|
||
async updateTypistGroup(
|
||
context: Context,
|
||
externalId: string,
|
||
typistGroupId: number,
|
||
typistGroupName: string,
|
||
typistIds: number[],
|
||
): Promise<void> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.updateTypistGroup.name
|
||
} | params: { ` +
|
||
`externalId: ${externalId}, ` +
|
||
`typistGroupId: ${typistGroupId}, ` +
|
||
`typistGroupName: ${typistGroupName}, ` +
|
||
`typistIds: ${typistIds} };`,
|
||
);
|
||
try {
|
||
// 外部IDをもとにユーザー情報を取得する
|
||
const { account_id } = await this.usersRepository.findUserByExternalId(
|
||
context,
|
||
externalId,
|
||
);
|
||
|
||
// タイピストグループと所属するタイピストを更新する
|
||
await this.userGroupsRepository.updateTypistGroup(
|
||
context,
|
||
account_id,
|
||
typistGroupId,
|
||
typistGroupName,
|
||
typistIds,
|
||
);
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
if (e instanceof Error) {
|
||
switch (e.constructor) {
|
||
// タイピストIDが存在しない場合は400エラーを返す
|
||
case TypistIdInvalidError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010204'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
// タイピストグループIDが存在しない場合は400エラーを返す
|
||
case TypistGroupNotExistError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010908'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
default:
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.updateTypistGroup.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* ライセンス発行をキャンセルする
|
||
* @param context
|
||
* @param extarnalId
|
||
* @param poNumber
|
||
* @param orderedAccountId
|
||
*/
|
||
async cancelIssue(
|
||
context: Context,
|
||
extarnalId: string,
|
||
poNumber: string,
|
||
orderedAccountId: number,
|
||
): Promise<void> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.cancelIssue.name
|
||
} | params: { ` +
|
||
`extarnalId: ${extarnalId}, ` +
|
||
`poNumber: ${poNumber}, ` +
|
||
`orderedAccountId: ${orderedAccountId}, };`,
|
||
);
|
||
let myAccountId: number;
|
||
|
||
try {
|
||
// ユーザIDからアカウントIDを取得する
|
||
myAccountId = (
|
||
await this.usersRepository.findUserByExternalId(context, extarnalId)
|
||
).account_id;
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
switch (e.constructor) {
|
||
default:
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.cancelIssue.name}`,
|
||
);
|
||
}
|
||
|
||
// 注文元アカウントIDの親世代を取得
|
||
const parentAccountIds = await this.accountRepository.getHierarchyParents(
|
||
context,
|
||
orderedAccountId,
|
||
);
|
||
// 自身が存在しない場合、エラー
|
||
if (!parentAccountIds.includes(myAccountId)) {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.cancelIssue.name}`,
|
||
);
|
||
throw new HttpException(
|
||
makeErrorResponse('E000108'),
|
||
HttpStatus.UNAUTHORIZED,
|
||
);
|
||
}
|
||
|
||
try {
|
||
// 発行キャンセル処理
|
||
await this.accountRepository.cancelIssue(
|
||
context,
|
||
orderedAccountId,
|
||
poNumber,
|
||
);
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
switch (e.constructor) {
|
||
case AlreadyLicenseStatusChangedError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010809'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
case CancellationPeriodExpiredError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010810'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
case AlreadyLicenseAllocatedError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010811'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
default:
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.cancelIssue.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* ワークタイプ一覧を取得します
|
||
* @param context
|
||
* @param externalId
|
||
* @returns worktypes
|
||
*/
|
||
async getWorktypes(
|
||
context: Context,
|
||
externalId: string,
|
||
): Promise<GetWorktypesResponse> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.getWorktypes.name
|
||
} | params: { externalId: ${externalId} };`,
|
||
);
|
||
try {
|
||
// 外部IDをもとにユーザー情報を取得する
|
||
const { account_id: accountId } =
|
||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||
|
||
// ワークタイプ一覧とActiveWorktypeIDを取得する
|
||
const { worktypes, active_worktype_id } =
|
||
await this.worktypesRepository.getWorktypes(context, accountId);
|
||
|
||
return {
|
||
worktypes: worktypes.map((x) => ({
|
||
id: x.id,
|
||
worktypeId: x.custom_worktype_id,
|
||
description: x.description ?? undefined,
|
||
})),
|
||
active: active_worktype_id,
|
||
};
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
if (e instanceof Error) {
|
||
switch (e.constructor) {
|
||
case AccountNotFoundError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010501'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
default:
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.getWorktypes.name}`,
|
||
);
|
||
}
|
||
}
|
||
/**
|
||
* ワークタイプを作成します
|
||
* @param context
|
||
* @param externalId
|
||
* @param worktypeId
|
||
* @param [description]
|
||
* @returns worktype
|
||
*/
|
||
async createWorktype(
|
||
context: Context,
|
||
externalId: string,
|
||
worktypeId: string,
|
||
description?: string,
|
||
): Promise<void> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.createWorktype.name
|
||
} | params: { externalId: ${externalId}, worktypeId: ${worktypeId}, description: ${description} };`,
|
||
);
|
||
|
||
try {
|
||
// 外部IDをもとにユーザー情報を取得する
|
||
const { account_id: accountId } =
|
||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||
|
||
await this.worktypesRepository.createWorktype(
|
||
context,
|
||
accountId,
|
||
worktypeId,
|
||
description,
|
||
);
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
if (e instanceof Error) {
|
||
switch (e.constructor) {
|
||
// WorktypeIDが既に存在する場合は400エラーを返す
|
||
case WorktypeIdAlreadyExistsError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E011001'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
// WorktypeIDが登録上限以上の場合は400エラーを返す
|
||
case WorktypeIdMaxCountError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E011002'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
default:
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.createWorktype.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* ワークタイプを更新します
|
||
* @param context
|
||
* @param externalId
|
||
* @param id ワークタイプの内部ID
|
||
* @param worktypeId ユーザーが設定するワークタイプ名
|
||
* @param [description]
|
||
* @returns worktype
|
||
*/
|
||
async updateWorktype(
|
||
context: Context,
|
||
externalId: string,
|
||
id: number,
|
||
worktypeId: string,
|
||
description?: string,
|
||
): Promise<void> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.updateWorktype.name
|
||
} | params: { ` +
|
||
`externalId: ${externalId}, ` +
|
||
`id: ${id}, ` +
|
||
`worktypeId: ${worktypeId}, ` +
|
||
`description: ${description} };`,
|
||
);
|
||
|
||
try {
|
||
// 外部IDをもとにユーザー情報を取得する
|
||
const { account_id: accountId } =
|
||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||
|
||
// ワークタイプを更新する
|
||
await this.worktypesRepository.updateWorktype(
|
||
context,
|
||
accountId,
|
||
id,
|
||
worktypeId,
|
||
description,
|
||
);
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
if (e instanceof Error) {
|
||
switch (e.constructor) {
|
||
// ユーザーが設定したWorktypeIDが既存WorktypeのWorktypeIDと重複する場合は400エラーを返す
|
||
case WorktypeIdAlreadyExistsError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E011001'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
// 内部IDで指定されたWorktypeが存在しない場合は400エラーを返す
|
||
case WorktypeIdNotFoundError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E011003'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
default:
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.updateWorktype.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* ワークタイプを削除します
|
||
* @param context
|
||
* @param externalId
|
||
* @param id
|
||
* @returns worktype
|
||
*/
|
||
async deleteWorktype(
|
||
context: Context,
|
||
externalId: string,
|
||
id: number,
|
||
): Promise<void> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.deleteWorktype.name
|
||
} | params: { ` +
|
||
`externalId: ${externalId}, ` +
|
||
`id: ${id} };`,
|
||
);
|
||
|
||
try {
|
||
// 外部IDをもとにユーザー情報を取得する
|
||
const { account, account_id: accountId } =
|
||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||
|
||
if (!account) {
|
||
throw new AccountNotFoundError(
|
||
`account not found. externalId: ${externalId}`,
|
||
);
|
||
}
|
||
|
||
// ワークタイプを削除する
|
||
await this.worktypesRepository.deleteWorktype(context, accountId, id);
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
if (e instanceof Error) {
|
||
switch (e.constructor) {
|
||
case UserNotFoundError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010204'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
case AccountNotFoundError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010501'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
// 内部IDで指定されたWorktypeが存在しない場合は400エラーを返す
|
||
case WorktypeIdNotFoundError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E011003'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
// 内部IDで指定されたWorktypeがWorkflowで使用中の場合は400エラーを返す
|
||
case WorktypeIdInUseError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E011004'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
default:
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.deleteWorktype.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* ワークタイプに紐づいたオプションアイテム一覧を取得します
|
||
* @param context
|
||
* @param externalId
|
||
* @param id Worktypeの内部ID
|
||
* @returns option items
|
||
*/
|
||
async getOptionItems(
|
||
context: Context,
|
||
externalId: string,
|
||
id: number,
|
||
): Promise<GetOptionItemsResponse> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.getOptionItems.name
|
||
} | params: { ` +
|
||
`externalId: ${externalId}, ` +
|
||
`id: ${id} };`,
|
||
);
|
||
try {
|
||
// 外部IDをもとにユーザー情報を取得する
|
||
const { account_id: accountId } =
|
||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||
|
||
// オプションアイテム一覧を取得する
|
||
const optionItems = await this.worktypesRepository.getOptionItems(
|
||
context,
|
||
accountId,
|
||
id,
|
||
);
|
||
|
||
return {
|
||
optionItems: optionItems.map((x) => ({
|
||
id: x.id,
|
||
itemLabel: x.item_label,
|
||
defaultValueType: x.default_value_type,
|
||
initialValue: x.initial_value,
|
||
})),
|
||
};
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
if (e instanceof Error) {
|
||
switch (e.constructor) {
|
||
// 内部IDで指定されたWorktypeが存在しない場合は400エラーを返す
|
||
case WorktypeIdNotFoundError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E011003'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
default:
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.getOptionItems.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* オプションアイテムを更新します
|
||
* @param context
|
||
* @param externalId
|
||
* @param id ワークタイプの内部ID
|
||
* @param optionItems
|
||
* @returns option items
|
||
*/
|
||
async updateOptionItems(
|
||
context: Context,
|
||
externalId: string,
|
||
id: number,
|
||
optionItems: PostWorktypeOptionItem[],
|
||
): Promise<void> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.updateOptionItems.name
|
||
} | params: { ` +
|
||
`externalId: ${externalId}, ` +
|
||
`id: ${id}, ` +
|
||
`optionItems: ${JSON.stringify(optionItems)} };`,
|
||
);
|
||
try {
|
||
// 外部IDをもとにユーザー情報を取得する
|
||
const { account_id: accountId } =
|
||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||
|
||
// オプションアイテムを更新する
|
||
await this.worktypesRepository.updateOptionItems(
|
||
context,
|
||
accountId,
|
||
id,
|
||
// initialValueはdefaultValueTypeがDEFAULTの場合以外は空文字を設定する
|
||
optionItems.map((item) => ({
|
||
itemLabel: item.itemLabel,
|
||
defaultValueType: item.defaultValueType,
|
||
initialValue:
|
||
item.defaultValueType === OPTION_ITEM_VALUE_TYPE.DEFAULT
|
||
? item.initialValue
|
||
: '',
|
||
})),
|
||
);
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
if (e instanceof Error) {
|
||
switch (e.constructor) {
|
||
// 内部IDで指定されたWorktypeが存在しない場合は400エラーを返す
|
||
case WorktypeIdNotFoundError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E011003'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
default:
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.updateOptionItems.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* ActiveWorktypeの更新
|
||
* @param context
|
||
* @param externalId
|
||
* @param id ActiveWorktypeの内部ID
|
||
* @returns active worktype
|
||
*/
|
||
async updateActiveWorktype(
|
||
context: Context,
|
||
externalId: string,
|
||
id: number | undefined,
|
||
): Promise<void> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.updateActiveWorktype.name
|
||
} | params: { ` +
|
||
`externalId: ${externalId}, ` +
|
||
`id: ${id} };`,
|
||
);
|
||
try {
|
||
// 外部IDをもとにユーザー情報を取得する
|
||
const { account_id: accountId } =
|
||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||
|
||
// ActiveWorktypeを更新する
|
||
await this.accountRepository.updateActiveWorktypeId(
|
||
context,
|
||
accountId,
|
||
id,
|
||
);
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
if (e instanceof Error) {
|
||
switch (e.constructor) {
|
||
// 内部IDで指定されたWorktypeが存在しない場合は400エラーを返す
|
||
case WorktypeIdNotFoundError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E011003'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
default:
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.updateActiveWorktype.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* パートナー一覧を取得します
|
||
* @param context
|
||
* @param externalId
|
||
* @param limit
|
||
* @param offset
|
||
* @returns GetPartnersResponse
|
||
*/
|
||
async getPartners(
|
||
context: Context,
|
||
externalId: string,
|
||
limit: number,
|
||
offset: number,
|
||
): Promise<GetPartnersResponse> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.getPartners.name
|
||
} | params: { ` +
|
||
`externalId: ${externalId}, ` +
|
||
`limit: ${limit}, ` +
|
||
`offset: ${offset}, };`,
|
||
);
|
||
|
||
try {
|
||
const { account_id: accountId } =
|
||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||
|
||
const partnersRecords = await this.accountRepository.getPartners(
|
||
context,
|
||
accountId,
|
||
limit,
|
||
offset,
|
||
);
|
||
|
||
// DBから取得したユーザーの外部IDをもとにADB2Cからユーザーを取得する
|
||
let externalIds = partnersRecords.partnersInfo.map(
|
||
(x) => x.primaryAccountExternalId,
|
||
);
|
||
externalIds = externalIds.filter((item) => item !== undefined);
|
||
const adb2cUsers = await this.adB2cService.getUsers(context, externalIds);
|
||
|
||
// DBから取得した情報とADB2Cから取得した情報をマージ
|
||
const partners = partnersRecords.partnersInfo.map((db): Partner => {
|
||
const adb2cUser = adb2cUsers.find(
|
||
(adb2c) => db.primaryAccountExternalId === adb2c.id,
|
||
);
|
||
if (!adb2cUser) {
|
||
throw new Error(
|
||
`adb2c user not found. externalId: ${db.primaryAccountExternalId}`,
|
||
);
|
||
}
|
||
|
||
const primaryAdmin = adb2cUser.displayName;
|
||
const mail = adb2cUser.identities?.find(
|
||
(identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
|
||
)?.issuerAssignedId;
|
||
if (!mail) {
|
||
throw new Error(
|
||
`adb2c user mail not found. externalId: ${db.primaryAccountExternalId}`,
|
||
);
|
||
}
|
||
|
||
return {
|
||
name: db.name,
|
||
tier: db.tier,
|
||
accountId: db.accountId,
|
||
country: db.country,
|
||
primaryAdmin: primaryAdmin,
|
||
email: mail,
|
||
dealerManagement: db.dealerManagement,
|
||
};
|
||
});
|
||
|
||
return {
|
||
total: partnersRecords.total,
|
||
partners: partners,
|
||
};
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.getPartners.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* アカウント情報を設定する
|
||
* @param context
|
||
* @param externalId
|
||
* @param tier
|
||
* @param delegationPermission
|
||
* @param primaryAdminUserId
|
||
* @param parentAccountId
|
||
* @param secondryAdminUserId
|
||
* @returns UpdateAccountInfoResponse
|
||
*/
|
||
async updateAccountInfo(
|
||
context: Context,
|
||
externalId: string,
|
||
tier: number,
|
||
delegationPermission: boolean,
|
||
primaryAdminUserId: number,
|
||
parentAccountId?: number,
|
||
secondryAdminUserId?: number,
|
||
): Promise<void> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.updateAccountInfo.name
|
||
} | params: { ` +
|
||
`externalId: ${externalId}, ` +
|
||
`tier: ${tier}, ` +
|
||
`delegationPermission: ${delegationPermission}, ` +
|
||
`primaryAdminUserId: ${primaryAdminUserId}, ` +
|
||
`parentAccountId: ${parentAccountId}, ` +
|
||
`secondryAdminUserId: ${secondryAdminUserId}, };`,
|
||
);
|
||
|
||
try {
|
||
const { account_id: accountId } =
|
||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||
await this.accountRepository.updateAccountInfo(
|
||
context,
|
||
accountId,
|
||
tier,
|
||
delegationPermission,
|
||
primaryAdminUserId,
|
||
parentAccountId,
|
||
secondryAdminUserId,
|
||
);
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
if (e instanceof Error) {
|
||
switch (e.constructor) {
|
||
case DealerAccountNotFoundError:
|
||
case AdminUserNotFoundError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010502'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
default:
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.updateAccountInfo.name}`,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* アカウントと紐づくデータを削除する
|
||
* @param context
|
||
* @param externalId
|
||
* @param accountId // 削除対象のアカウントID
|
||
*/
|
||
async deleteAccountAndData(
|
||
context: Context,
|
||
externalId: string,
|
||
accountId: number,
|
||
): Promise<void> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.deleteAccountAndData.name
|
||
} | params: { ` +
|
||
`externalId: ${externalId}, ` +
|
||
`accountId: ${accountId}, };`,
|
||
);
|
||
let country: string;
|
||
let dbUsers: User[];
|
||
try {
|
||
// パラメータとトークンから取得したアカウントIDの突き合わせ
|
||
const { account_id: myAccountId } =
|
||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||
if (myAccountId !== accountId) {
|
||
throw new HttpException(
|
||
makeErrorResponse('E000108'),
|
||
HttpStatus.UNAUTHORIZED,
|
||
);
|
||
}
|
||
|
||
// アカウント削除前に必要な情報を退避する
|
||
const targetAccount = await this.accountRepository.findAccountById(
|
||
context,
|
||
accountId,
|
||
);
|
||
// 削除対象アカウントを削除する
|
||
dbUsers = await this.accountRepository.deleteAccountAndInsertArchives(
|
||
context,
|
||
accountId,
|
||
);
|
||
this.logger.log(
|
||
`[${context.getTrackingId()}] delete account: ${accountId}`,
|
||
);
|
||
country = targetAccount.country;
|
||
} catch (e) {
|
||
// アカウントの削除に失敗した場合はエラーを返す
|
||
this.logger.log(`[${context.getTrackingId()}] ${e}`);
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.deleteAccountAndData.name}`,
|
||
);
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
|
||
try {
|
||
// 削除対象アカウント内のADB2Cユーザーをすべて削除する
|
||
await this.adB2cService.deleteUsers(
|
||
dbUsers.map((x) => x.external_id),
|
||
context,
|
||
);
|
||
this.logger.log(
|
||
`[${context.getTrackingId()}] delete ADB2C users: ${accountId}, users_id: ${dbUsers.map(
|
||
(x) => x.external_id,
|
||
)}`,
|
||
);
|
||
} catch (e) {
|
||
// ADB2Cユーザーの削除失敗時は、MANUAL_RECOVERY_REQUIREDを出して処理続行
|
||
this.logger.log(`[${context.getTrackingId()}] ${e}`);
|
||
this.logger.log(
|
||
`${MANUAL_RECOVERY_REQUIRED} [${context.getTrackingId()}] Failed to delete ADB2C users: ${accountId}, users_id: ${dbUsers.map(
|
||
(x) => x.external_id,
|
||
)}`,
|
||
);
|
||
}
|
||
|
||
try {
|
||
// blobstorageコンテナを削除する
|
||
await this.deleteBlobContainer(accountId, country, context);
|
||
this.logger.log(
|
||
`[${context.getTrackingId()}] delete blob container: ${accountId}-${country}`,
|
||
);
|
||
} catch (e) {
|
||
// blobstorageコンテナを削除で失敗した場合は、MANUAL_RECOVERY_REQUIRED出して正常終了
|
||
this.logger.log(`[${context.getTrackingId()}] ${e}`);
|
||
this.logger.log(
|
||
`${MANUAL_RECOVERY_REQUIRED}[${context.getTrackingId()}] Failed to delete blob container: ${accountId}, country: ${country}`,
|
||
);
|
||
}
|
||
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.deleteAccountAndData.name}`,
|
||
);
|
||
}
|
||
|
||
/**
|
||
* IDトークンのsubからアカウントの階層情報を取得します
|
||
* @param context
|
||
* @param externalId
|
||
* @returns account info minimal access
|
||
*/
|
||
async getAccountInfoMinimalAccess(
|
||
context: Context,
|
||
externalId: string,
|
||
): Promise<number> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.getAccountInfoMinimalAccess.name
|
||
} | params: { externalId: ${externalId} };`,
|
||
);
|
||
|
||
try {
|
||
const { account } = await this.usersRepository.findUserByExternalId(
|
||
context,
|
||
externalId,
|
||
);
|
||
if (!account) {
|
||
throw new AccountNotFoundError(
|
||
`Account not found. externalId: ${externalId}`,
|
||
);
|
||
}
|
||
|
||
return account.tier;
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
if (e instanceof Error) {
|
||
switch (e.constructor) {
|
||
case UserNotFoundError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010204'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
case AccountNotFoundError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010501'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
default:
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${
|
||
this.getAccountInfoMinimalAccess.name
|
||
}`,
|
||
);
|
||
}
|
||
}
|
||
/**
|
||
* 自アカウントの会社名を取得する
|
||
* @param accountId
|
||
* @returns CompanyName
|
||
*/
|
||
async getCompanyName(
|
||
context: Context,
|
||
accountId: number,
|
||
): Promise<GetCompanyNameResponse> {
|
||
this.logger.log(
|
||
`[IN] [${context.getTrackingId()}] ${
|
||
this.getCompanyName.name
|
||
} | params: { accountId: ${accountId}, };`,
|
||
);
|
||
|
||
try {
|
||
const { company_name } = await this.accountRepository.findAccountById(
|
||
context,
|
||
accountId,
|
||
);
|
||
|
||
return { companyName: company_name };
|
||
} catch (e) {
|
||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||
if (e instanceof Error) {
|
||
switch (e.constructor) {
|
||
case AccountNotFoundError:
|
||
throw new HttpException(
|
||
makeErrorResponse('E010501'),
|
||
HttpStatus.BAD_REQUEST,
|
||
);
|
||
default:
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
throw new HttpException(
|
||
makeErrorResponse('E009999'),
|
||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
} finally {
|
||
this.logger.log(
|
||
`[OUT] [${context.getTrackingId()}] ${this.getCompanyName.name}`,
|
||
);
|
||
}
|
||
}
|
||
}
|