diff --git a/dictation_server/src/features/accounts/accounts.service.ts b/dictation_server/src/features/accounts/accounts.service.ts index 373e7bc..ba76c63 100644 --- a/dictation_server/src/features/accounts/accounts.service.ts +++ b/dictation_server/src/features/accounts/accounts.service.ts @@ -71,6 +71,7 @@ import { WorktypeIdMaxCountError, WorktypeIdNotFoundError, } from '../../repositories/worktypes/errors/types'; +import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils'; @Injectable() export class AccountsService { @@ -1930,10 +1931,8 @@ export class AccountsService { ); } - const primaryAdmin = adb2cUser.displayName; - const mail = adb2cUser.identities?.find( - (identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS, - )?.issuerAssignedId; + const { displayName: primaryAdmin, emailAddress: mail } = + getUserNameAndMailAddress(adb2cUser); if (!mail) { throw new Error( `adb2c user mail not found. externalId: ${db.primaryAccountExternalId}`, diff --git a/dictation_server/src/features/licenses/licenses.module.ts b/dictation_server/src/features/licenses/licenses.module.ts index d3a69a4..b18272a 100644 --- a/dictation_server/src/features/licenses/licenses.module.ts +++ b/dictation_server/src/features/licenses/licenses.module.ts @@ -4,12 +4,16 @@ import { LicensesService } from './licenses.service'; import { UsersRepositoryModule } from '../../repositories/users/users.repository.module'; import { AccountsRepositoryModule } from '../../repositories/accounts/accounts.repository.module'; import { LicensesRepositoryModule } from '../../repositories/licenses/licenses.repository.module'; +import { AdB2cModule } from '../../gateways/adb2c/adb2c.module'; +import { SendGridModule } from '../../gateways/sendgrid/sendgrid.module'; @Module({ imports: [ UsersRepositoryModule, AccountsRepositoryModule, LicensesRepositoryModule, + AdB2cModule, + SendGridModule, ], controllers: [LicensesController], providers: [LicensesService], diff --git a/dictation_server/src/features/licenses/licenses.service.spec.ts b/dictation_server/src/features/licenses/licenses.service.spec.ts index 26efeae..f7d0e49 100644 --- a/dictation_server/src/features/licenses/licenses.service.spec.ts +++ b/dictation_server/src/features/licenses/licenses.service.spec.ts @@ -42,271 +42,6 @@ import { makeTestUser, } from '../../common/test/utility'; -describe('LicensesService', () => { - it('ライセンス注文が完了する', async () => { - const lisencesRepositoryMockValue = - makeDefaultLicensesRepositoryMockValue(); - const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); - const accountsRepositoryMockValue = - makeDefaultAccountsRepositoryMockValue(); - const service = await makeLicensesServiceMock( - lisencesRepositoryMockValue, - usersRepositoryMockValue, - accountsRepositoryMockValue, - ); - const body = new CreateOrdersRequest(); - const userId = '0001'; - body.orderCount = 1000; - body.poNumber = '1'; - const context = makeContext(`uuidv4`, 'requestId'); - expect( - await service.licenseOrders( - context, - userId, - body.poNumber, - body.orderCount, - ), - ).toEqual(undefined); - }); - it('ユーザID取得できなかった場合、エラーとなる', async () => { - const lisencesRepositoryMockValue = - makeDefaultLicensesRepositoryMockValue(); - const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); - usersRepositoryMockValue.findUserByExternalId = new Error( - 'User not Found Error.', - ); - const accountsRepositoryMockValue = - makeDefaultAccountsRepositoryMockValue(); - const service = await makeLicensesServiceMock( - lisencesRepositoryMockValue, - usersRepositoryMockValue, - accountsRepositoryMockValue, - ); - const body = new CreateOrdersRequest(); - const userId = ''; - body.orderCount = 1000; - body.poNumber = '1'; - const context = makeContext(`uuidv4`, 'requestId'); - await expect( - service.licenseOrders(context, userId, body.poNumber, body.orderCount), - ).rejects.toEqual( - new HttpException( - makeErrorResponse('E009999'), - HttpStatus.INTERNAL_SERVER_ERROR, - ), - ); - }); - it('親ユーザID取得できなかった場合、エラーとなる', async () => { - const lisencesRepositoryMockValue = - makeDefaultLicensesRepositoryMockValue(); - const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); - usersRepositoryMockValue.findUserByExternalId = new Error( - 'Account not Found Error.', - ); - const accountsRepositoryMockValue = - makeDefaultAccountsRepositoryMockValue(); - const service = await makeLicensesServiceMock( - lisencesRepositoryMockValue, - usersRepositoryMockValue, - accountsRepositoryMockValue, - ); - const body = new CreateOrdersRequest(); - const userId = '0001'; - body.orderCount = 1000; - body.poNumber = '1'; - const context = makeContext(`uuidv4`, 'requestId'); - await expect( - service.licenseOrders(context, userId, body.poNumber, body.orderCount), - ).rejects.toEqual( - new HttpException( - makeErrorResponse('E009999'), - HttpStatus.INTERNAL_SERVER_ERROR, - ), - ); - }); - it('POナンバー重複時、エラーとなる', async () => { - const lisencesRepositoryMockValue = - makeDefaultLicensesRepositoryMockValue(); - lisencesRepositoryMockValue.order = new PoNumberAlreadyExistError( - `This PoNumber already used`, - ); - const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); - const accountsRepositoryMockValue = - makeDefaultAccountsRepositoryMockValue(); - const service = await makeLicensesServiceMock( - lisencesRepositoryMockValue, - usersRepositoryMockValue, - accountsRepositoryMockValue, - ); - const body = new CreateOrdersRequest(); - const userId = '0001'; - body.orderCount = 1000; - body.poNumber = '1'; - const context = makeContext(`uuidv4`, 'requestId'); - await expect( - service.licenseOrders(context, userId, body.poNumber, body.orderCount), - ).rejects.toEqual( - new HttpException( - makeErrorResponse('E010401'), - HttpStatus.INTERNAL_SERVER_ERROR, - ), - ); - }); - it('カードライセンス発行が完了する', async () => { - const lisencesRepositoryMockValue = - makeDefaultLicensesRepositoryMockValue(); - const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); - const accountsRepositoryMockValue = - makeDefaultAccountsRepositoryMockValue(); - const service = await makeLicensesServiceMock( - lisencesRepositoryMockValue, - usersRepositoryMockValue, - accountsRepositoryMockValue, - ); - const body = new IssueCardLicensesRequest(); - const userId = '0001'; - body.createCount = 10; - const issueCardLicensesResponse: IssueCardLicensesResponse = { - cardLicenseKeys: [ - 'WZCETXC0Z9PQZ9GKRGGY', - 'F0JD7EZEDBH4PQRQ83YF', - 'H0HXBP5K9RW7T7JSVDJV', - 'HKIWX54EESYL4X132223', - '363E81JR460UBHXGFXFI', - '70IKAPV9K6YMEVLTOXBY', - '1RJY1TRRYYTGF1LL9WLU', - 'BXM0HKFO7IULTL0A1B36', - 'XYLEWNY2LR6Q657CZE41', - 'AEJWRFFSWRQYQQJ6WVLV', - ], - }; - const context = makeContext(`uuidv4`, 'requestId'); - expect( - await service.issueCardLicenseKeys(context, userId, body.createCount), - ).toEqual(issueCardLicensesResponse); - }); - it('カードライセンス発行に失敗した場合、エラーになる', async () => { - const lisencesRepositoryMockValue = - makeDefaultLicensesRepositoryMockValue(); - lisencesRepositoryMockValue.createCardLicenses = new Error('DB failed'); - const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); - const accountsRepositoryMockValue = - makeDefaultAccountsRepositoryMockValue(); - const service = await makeLicensesServiceMock( - lisencesRepositoryMockValue, - usersRepositoryMockValue, - accountsRepositoryMockValue, - ); - const body = new IssueCardLicensesRequest(); - const userId = '0001'; - body.createCount = 1000; - const context = makeContext(`uuidv4`, 'requestId'); - await expect( - service.issueCardLicenseKeys(context, userId, body.createCount), - ).rejects.toEqual( - new HttpException( - makeErrorResponse('E009999'), - HttpStatus.INTERNAL_SERVER_ERROR, - ), - ); - }); - it('カードライセンス取り込みが完了する', async () => { - const lisencesRepositoryMockValue = - makeDefaultLicensesRepositoryMockValue(); - const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); - const accountsRepositoryMockValue = - makeDefaultAccountsRepositoryMockValue(); - const service = await makeLicensesServiceMock( - lisencesRepositoryMockValue, - usersRepositoryMockValue, - accountsRepositoryMockValue, - ); - const body = new ActivateCardLicensesRequest(); - const userId = '0001'; - body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY'; - const context = makeContext(`uuidv4`, 'requestId'); - expect( - await service.activateCardLicenseKey( - context, - userId, - body.cardLicenseKey, - ), - ).toEqual(undefined); - }); - it('カードライセンス取り込みに失敗した場合、エラーになる(DBエラー)', async () => { - const lisencesRepositoryMockValue = - makeDefaultLicensesRepositoryMockValue(); - lisencesRepositoryMockValue.activateCardLicense = new Error('DB failed'); - const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); - const accountsRepositoryMockValue = - makeDefaultAccountsRepositoryMockValue(); - const service = await makeLicensesServiceMock( - lisencesRepositoryMockValue, - usersRepositoryMockValue, - accountsRepositoryMockValue, - ); - const body = new ActivateCardLicensesRequest(); - const userId = '0001'; - body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY'; - const context = makeContext(`uuidv4`, 'requestId'); - await expect( - service.activateCardLicenseKey(context, userId, body.cardLicenseKey), - ).rejects.toEqual( - new HttpException( - makeErrorResponse('E009999'), - HttpStatus.INTERNAL_SERVER_ERROR, - ), - ); - }); - it('カードライセンス取り込みに失敗した場合、エラーになる(ライセンスが存在しないエラー)', async () => { - const lisencesRepositoryMockValue = - makeDefaultLicensesRepositoryMockValue(); - lisencesRepositoryMockValue.activateCardLicense = new LicenseNotExistError( - `License not exist`, - ); - const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); - const accountsRepositoryMockValue = - makeDefaultAccountsRepositoryMockValue(); - const service = await makeLicensesServiceMock( - lisencesRepositoryMockValue, - usersRepositoryMockValue, - accountsRepositoryMockValue, - ); - const body = new ActivateCardLicensesRequest(); - const userId = '0001'; - body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY'; - const context = makeContext(`uuidv4`, 'requestId'); - await expect( - service.activateCardLicenseKey(context, userId, body.cardLicenseKey), - ).rejects.toEqual( - new HttpException(makeErrorResponse('E010801'), HttpStatus.BAD_REQUEST), - ); - }); - it('カードライセンス取り込みに失敗した場合、エラーになる(ライセンスが既に取り込まれているエラー)', async () => { - const lisencesRepositoryMockValue = - makeDefaultLicensesRepositoryMockValue(); - lisencesRepositoryMockValue.activateCardLicense = - new LicenseKeyAlreadyActivatedError(`License already activated`); - const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); - const accountsRepositoryMockValue = - makeDefaultAccountsRepositoryMockValue(); - const service = await makeLicensesServiceMock( - lisencesRepositoryMockValue, - usersRepositoryMockValue, - accountsRepositoryMockValue, - ); - const body = new ActivateCardLicensesRequest(); - const userId = '0001'; - body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY'; - const context = makeContext(`uuidv4`, 'requestId'); - await expect( - service.activateCardLicenseKey(context, userId, body.cardLicenseKey), - ).rejects.toEqual( - new HttpException(makeErrorResponse('E010802'), HttpStatus.BAD_REQUEST), - ); - }); -}); - describe('DBテスト', () => { let source: DataSource | null = null; beforeEach(async () => { diff --git a/dictation_server/src/features/licenses/licenses.service.ts b/dictation_server/src/features/licenses/licenses.service.ts index 17e61bd..5e981df 100644 --- a/dictation_server/src/features/licenses/licenses.service.ts +++ b/dictation_server/src/features/licenses/licenses.service.ts @@ -16,6 +16,10 @@ import { IssueCardLicensesResponse, } from './types/types'; import { Context } from '../../common/log'; +import { SendGridService } from '../../gateways/sendgrid/sendgrid.service'; +import { AdB2cService } from '../../gateways/adb2c/adb2c.service'; +import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils'; +import { LICENSE_ISSUE_STATUS } from '../../constants'; @Injectable() export class LicensesService { @@ -23,6 +27,8 @@ export class LicensesService { private readonly usersRepository: UsersRepositoryService, private readonly accountsRepository: AccountsRepositoryService, private readonly licensesRepository: LicensesRepositoryService, + private readonly adb2cService: AdB2cService, + private readonly sendgridService: SendGridService, ) {} private readonly logger = new Logger(LicensesService.name); @@ -325,7 +331,71 @@ export class LicensesService { await this.usersRepository.findUserByExternalId(context, externalId) ).account_id; // 注文キャンセル処理 - await this.licensesRepository.cancelOrder(context, myAccountId, poNumber); + const { canceledOrderId } = await this.licensesRepository.cancelOrder(context, myAccountId, poNumber); + + // メール送信処理 + try { + // 注文キャンセルを実行したカスタマー企業の管理者情報を取得する + const customerAccountId = myAccountId; + + // カスタマー企業の企業名と管理者情報を取得する + const { + companyName: customerCompanyName, + adminEmails: customerAdminEmails, + } = await this.getAccountInformation(context, customerAccountId); + + // ディーラー企業を特定する + const { parent_account_id: dealerAccountId } = + await this.accountsRepository.findAccountById( + context, + customerAccountId, + ); + // ライセンス発行が行われているので、ディーラーは必ず存在するはず + if (dealerAccountId == null) { + throw new Error('dealerAccountId is null'); + } + + // ディーラー企業の企業名と管理者情報を取得する + const { + companyName: dealerCompanyName, + adminEmails: dealerAdminEmails, + } = await this.getAccountInformation(context, dealerAccountId); + + // キャンセル済みの注文をID指定して取得する + const order = await this.licensesRepository.getLicenseOrder( + context, + customerAccountId, + poNumber, + canceledOrderId + ); + + if (order == null) { + throw new Error( + `cancel target order not found. fromAccountId: ${customerAccountId}, poNumber:${poNumber}`, + ); + } + if (order.status !== LICENSE_ISSUE_STATUS.CANCELED) { + throw new Error( + `target order is not canceled. fromAccountId: ${order.from_account_id}, poNumber:${order.po_number}, status:${order.status}, id:${order.id}`, + ); + } + + const { quantity } = order; + + // 注文キャンセルが成功した旨をメール送信する + await this.sendgridService.sendMailWithU106( + context, + customerAdminEmails, + customerCompanyName, + quantity, + poNumber, + dealerAdminEmails, + dealerCompanyName, + ); + } catch (e) { + this.logger.error(`[${context.getTrackingId()}] error=${e}`); + // メール送信に関する例外はログだけ出して握りつぶす + } } catch (e) { this.logger.error(`[${context.getTrackingId()}] error=${e}`); switch (e.constructor) { @@ -352,4 +422,51 @@ export class LicensesService { } return; } + + /** + * アカウントIDを指定して、アカウント情報と管理者情報を取得する + * @param context + * @param accountId 対象アカウントID + * @returns 企業名/管理者メールアドレス + */ + private async getAccountInformation( + context: Context, + accountId: number, + ): Promise<{ + companyName: string; + adminEmails: string[]; + }> { + // アカウントIDから企業名を取得する + const { company_name } = await this.accountsRepository.findAccountById( + context, + accountId, + ); + + // 管理者一覧を取得 + const admins = await this.usersRepository.findAdminUsers( + context, + accountId, + ); + const adminExternalIDs = admins.map((x) => x.external_id); + + // ADB2Cから管理者IDを元にメールアドレスを取得する + const usersInfo = await this.adb2cService.getUsers( + context, + adminExternalIDs, + ); + + // 生のAzure AD B2Cのユーザー情報からメールアドレスを抽出する + const adminEmails = usersInfo.map((x) => { + const { emailAddress } = getUserNameAndMailAddress(x); + if (emailAddress == null) { + throw new Error('dealer admin email-address is not found'); + } + return emailAddress; + }); + + return { + companyName: company_name, + adminEmails: adminEmails, + }; + } } diff --git a/dictation_server/src/features/users/users.service.ts b/dictation_server/src/features/users/users.service.ts index e402d5b..9221ead 100644 --- a/dictation_server/src/features/users/users.service.ts +++ b/dictation_server/src/features/users/users.service.ts @@ -50,6 +50,7 @@ import { LicenseUnavailableError, } from '../../repositories/licenses/errors/types'; import { AccountNotFoundError } from '../../repositories/accounts/errors/types'; +import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils'; @Injectable() export class UsersService { @@ -570,10 +571,12 @@ export class UsersService { (user) => user.id === dbUser.external_id, ); + if (adb2cUser == null) { + throw new Error('mail not found.'); // TODO: リファクタ時に挙動を変更しないようエラー文面をmail not foundのまま据え置き。影響がない事が確認できたらエラー文面を変更する。 + } + // メールアドレスを取得する - const mail = adb2cUser?.identities?.find( - (identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS, - )?.issuerAssignedId; + const { emailAddress: mail } = getUserNameAndMailAddress(adb2cUser); //メールアドレスが取得できない場合はエラー if (!mail) { diff --git a/dictation_server/src/gateways/adb2c/utils/utils.ts b/dictation_server/src/gateways/adb2c/utils/utils.ts index 1d3b4a9..dfeb4c7 100644 --- a/dictation_server/src/gateways/adb2c/utils/utils.ts +++ b/dictation_server/src/gateways/adb2c/utils/utils.ts @@ -1,3 +1,6 @@ +import { ADB2C_SIGN_IN_TYPE } from "../../../constants"; +import { AdB2cUser } from "../types/types"; + export const isPromiseRejectedResult = ( data: unknown, ): data is PromiseRejectedResult => { @@ -8,3 +11,12 @@ export const isPromiseRejectedResult = ( 'reason' in data ); }; + +// 生のAdB2cUserのレスポンスから表示名とメールアドレスを取得する +export const getUserNameAndMailAddress = (user: AdB2cUser) => { + const { displayName, identities } = user; + const emailAddress = identities?.find( + (identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS, + )?.issuerAssignedId; + return { displayName, emailAddress }; +} \ No newline at end of file diff --git a/dictation_server/src/gateways/sendgrid/sendgrid.service.ts b/dictation_server/src/gateways/sendgrid/sendgrid.service.ts index 7874a72..dbb7776 100644 --- a/dictation_server/src/gateways/sendgrid/sendgrid.service.ts +++ b/dictation_server/src/gateways/sendgrid/sendgrid.service.ts @@ -137,6 +137,38 @@ export class SendGridService { }; } + /** + * U-106のテンプレートを使用したメールを送信する + * @param context + * @param cancelUserEmailAddress 注文キャンセルを行ったアカウントの管理者(primary/secondary)のメールアドレス + * @param customerAccountName 送信対象の企業名 + * @param lisenceCount 注文キャンセルを行った対象の注文の内容(ライセンス数) + * @param poNumber 注文キャンセルを行った対象の注文の内容(PO番号) + * @param dealerEmails 問題発生時に問い合わせする先の上位のディーラーの管理者(primary/secondary)のメールアドレス + * @param dealerAccountName 問題発生時に問い合わせする先の上位のディーラー名(会社名) + * @returns mail with u106 + */ + async sendMailWithU106( + context: Context, + customerMails: string[], + customerAccountName: string, + lisenceCount: number, + poNumber: string, + dealerEmails: string[], + dealerAccountName: string, + ): Promise { + this.logger.log( + `[IN] [${context.getTrackingId()}] ${this.sendMailWithU106.name}`, + ); + try { + // NOT IMPLEMENT + } finally { + this.logger.log( + `[OUT] [${context.getTrackingId()}] ${this.sendMailWithU106.name}`, + ); + } + } + /** * メールを送信する * @param context diff --git a/dictation_server/src/repositories/licenses/licenses.repository.service.ts b/dictation_server/src/repositories/licenses/licenses.repository.service.ts index e9f2ad0..523f004 100644 --- a/dictation_server/src/repositories/licenses/licenses.repository.service.ts +++ b/dictation_server/src/repositories/licenses/licenses.repository.service.ts @@ -1,5 +1,5 @@ import { Injectable, Logger } from '@nestjs/common'; -import { DataSource, In } from 'typeorm'; +import { DataSource, EntityManager, In, Not } from 'typeorm'; import { LicenseOrder, License, @@ -101,6 +101,36 @@ export class LicensesRepositoryService { return createdEntity; } + /** + * オーダーIDとPO番号と依頼元アカウントIDを元にライセンス注文を取得する + * @param context context + * @param fromAccountId ライセンス注文を行ったアカウントのID + * @param poNumber PO番号 + * @param orderId LicenseOrderのID + * @returns license order + */ + async getLicenseOrder( + context: Context, + fromAccountId: number, + poNumber: string, + orderId: number, + ): Promise { + return await this.dataSource.transaction(async (entityManager) => { + const repo = entityManager.getRepository(LicenseOrder); + + // ステータスは問わず、指定したIDのランセンス注文を取得する + const entity = repo.findOne({ + where: { + id: orderId, + po_number: poNumber, + from_account_id: fromAccountId, + }, + comment: `${context.getTrackingId()}_${new Date().toUTCString()}`, + }); + return entity; + }); + } + /** * カードライセンスを発行する * @param accountId @@ -729,8 +759,8 @@ export class LicensesRepositoryService { context: Context, accountId: number, poNumber: string, - ): Promise { - await this.dataSource.transaction(async (entityManager) => { + ): Promise<{ canceledOrderId: number }> { + return await this.dataSource.transaction(async (entityManager) => { const orderRepo = entityManager.getRepository(LicenseOrder); // キャンセル対象の注文を取得 @@ -760,6 +790,8 @@ export class LicensesRepositoryService { this.isCommentOut, context, ); + + return { canceledOrderId: targetOrder.id }; }); } diff --git a/dictation_server/src/repositories/users/users.repository.service.ts b/dictation_server/src/repositories/users/users.repository.service.ts index abaa0d9..ca46c20 100644 --- a/dictation_server/src/repositories/users/users.repository.service.ts +++ b/dictation_server/src/repositories/users/users.repository.service.ts @@ -1,6 +1,12 @@ import { Injectable } from '@nestjs/common'; import { User, newUser } from './entity/user.entity'; -import { DataSource, IsNull, Not, UpdateResult } from 'typeorm'; +import { + DataSource, + FindOptionsWhere, + IsNull, + Not, + UpdateResult, +} from 'typeorm'; import { SortCriteria } from '../sort_criteria/entity/sort_criteria.entity'; import { getDirection, @@ -169,6 +175,40 @@ export class UsersRepositoryService { return user; } + /** + * 指定したアカウントIDを持つアカウントのプライマリ管理者とセカンダリ管理者を取得する + * @param context context + * @param accountId アカウントID + * @throws AccountNotFoundError + * @returns admin users + */ + async findAdminUsers(context: Context, accountId: number): Promise { + return this.dataSource.transaction(async (entityManager) => { + const account = await entityManager.getRepository(Account).findOne({ + where: { + id: accountId, + }, + comment: `${context.getTrackingId()}_${new Date().toUTCString()}`, + }); + if (account == null) { + throw new AccountNotFoundError('account not found'); + } + const primaryAdminId = account.primary_admin_user_id; + const secondaryAdminId = account.secondary_admin_user_id; + + // IDが有効なユーザーだけを検索対象とする + const targets = [primaryAdminId, secondaryAdminId] + .flatMap((x) => (x == null ? [] : [x])) + .map((x): FindOptionsWhere => ({ id: x })); + + const users = await entityManager.getRepository(User).find({ + where: targets, + comment: `${context.getTrackingId()}_${new Date().toUTCString()}`, + }); + return users; + }); + } + /** * AuthorIdが既に存在するか確認する * @param user