Merged PR 797: API実装(一括登録)
## 概要 [Task3752: API実装(一括登録)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3752) - ユーザー一括登録APIとテストを実装しました。 - メール文面はまだ翻訳が来ていないので日本語のものを使用しています。別タスクで多言語対応します。 ## レビューポイント - ファイル名は認識通りでしょうか? - ファイルの内容は認識通りでしょうか? ## UIの変更 - なし ## 動作確認状況 - ローカルで確認
This commit is contained in:
parent
7ff563f644
commit
31de71f743
@ -20,12 +20,15 @@ STORAGE_TOKEN_EXPIRE_TIME=30
|
||||
STORAGE_ACCOUNT_NAME_US=saxxxxusxxx
|
||||
STORAGE_ACCOUNT_NAME_AU=saxxxxauxxx
|
||||
STORAGE_ACCOUNT_NAME_EU=saxxxxeuxxx
|
||||
STORAGE_ACCOUNT_NAME_IMPORTS=saxxxximportsxxx
|
||||
STORAGE_ACCOUNT_KEY_US=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==
|
||||
STORAGE_ACCOUNT_KEY_AU=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==
|
||||
STORAGE_ACCOUNT_KEY_EU=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==
|
||||
STORAGE_ACCOUNT_KEY_IMPORTS=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==
|
||||
STORAGE_ACCOUNT_ENDPOINT_US=https://xxxxxxxxxxxx.blob.core.windows.net/
|
||||
STORAGE_ACCOUNT_ENDPOINT_AU=https://xxxxxxxxxxxx.blob.core.windows.net/
|
||||
STORAGE_ACCOUNT_ENDPOINT_EU=https://xxxxxxxxxxxx.blob.core.windows.net/
|
||||
STORAGE_ACCOUNT_ENDPOINT_IMPORTS=https://xxxxxxxxxxxx.blob.core.windows.net/
|
||||
ACCESS_TOKEN_LIFETIME_WEB=7200000
|
||||
REFRESH_TOKEN_LIFETIME_WEB=86400000
|
||||
REFRESH_TOKEN_LIFETIME_DEFAULT=2592000000
|
||||
|
||||
@ -180,6 +180,11 @@ export const overrideBlobstorageService = <TService>(
|
||||
accountId: number,
|
||||
country: string,
|
||||
) => Promise<string>;
|
||||
uploadImportsBlob?: (
|
||||
context: Context,
|
||||
fileName: string,
|
||||
content: string,
|
||||
) => Promise<void>;
|
||||
},
|
||||
): void => {
|
||||
// テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得
|
||||
@ -220,6 +225,12 @@ export const overrideBlobstorageService = <TService>(
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
if (overrides.uploadImportsBlob) {
|
||||
Object.defineProperty(obj, obj.uploadImportsBlob.name, {
|
||||
value: overrides.uploadImportsBlob,
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -19,6 +19,7 @@ import {
|
||||
import { AdB2cUser } from '../../../gateways/adb2c/types/types';
|
||||
import { ADB2C_SIGN_IN_TYPE } from '../../../constants';
|
||||
import { AccountsRepositoryService } from '../../../repositories/accounts/accounts.repository.service';
|
||||
import { BlobstorageService } from '../../../gateways/blobstorage/blobstorage.service';
|
||||
|
||||
export type SortCriteriaRepositoryMockValue = {
|
||||
updateSortCriteria: SortCriteria | Error;
|
||||
@ -89,6 +90,8 @@ export const makeUsersServiceMock = async (
|
||||
return makeSortCriteriaRepositoryMock(
|
||||
sortCriteriaRepositoryMockValue,
|
||||
);
|
||||
case BlobstorageService:
|
||||
return {};
|
||||
}
|
||||
})
|
||||
.compile();
|
||||
|
||||
@ -4,7 +4,6 @@ import {
|
||||
Get,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Ip,
|
||||
Logger,
|
||||
Post,
|
||||
Query,
|
||||
@ -1068,7 +1067,9 @@ export class UsersController {
|
||||
const context = makeContext(userId, requestId, delegateUserId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
// TODO: 処理を実装
|
||||
// 登録処理
|
||||
const { users, filename } = body;
|
||||
await this.usersService.multipleImports(context, userId, filename, users);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import { UsersController } from './users.controller';
|
||||
import { UsersService } from './users.service';
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { AccountsRepositoryModule } from '../../repositories/accounts/accounts.repository.module';
|
||||
import { BlobstorageModule } from '../../gateways/blobstorage/blobstorage.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -19,6 +20,7 @@ import { AccountsRepositoryModule } from '../../repositories/accounts/accounts.r
|
||||
AdB2cModule,
|
||||
SendGridModule,
|
||||
ConfigModule,
|
||||
BlobstorageModule,
|
||||
],
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService, AuthService],
|
||||
|
||||
@ -31,6 +31,7 @@ import { makeTestingModule } from '../../common/test/modules';
|
||||
import { Context, makeContext } from '../../common/log';
|
||||
import {
|
||||
overrideAdB2cService,
|
||||
overrideBlobstorageService,
|
||||
overrideSendgridService,
|
||||
overrideUsersRepositoryService,
|
||||
} from '../../common/test/overrides';
|
||||
@ -3656,6 +3657,206 @@ describe('UsersService.deleteUser', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('UsersService.multipleImports', () => {
|
||||
let source: DataSource | null = null;
|
||||
|
||||
beforeAll(async () => {
|
||||
if (source == null) {
|
||||
source = await (async () => {
|
||||
const s = new DataSource({
|
||||
type: 'mysql',
|
||||
host: 'test_mysql_db',
|
||||
port: 3306,
|
||||
username: 'user',
|
||||
password: 'password',
|
||||
database: 'odms',
|
||||
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
|
||||
synchronize: false, // trueにすると自動的にmigrationが行われるため注意
|
||||
});
|
||||
return await s.initialize();
|
||||
})();
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
if (source) {
|
||||
await truncateAllTable(source);
|
||||
}
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await source?.destroy();
|
||||
source = null;
|
||||
});
|
||||
|
||||
it('ユーザー一括登録ができる', async () => {
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
|
||||
const { account: dealer } = await makeTestAccount(source, {
|
||||
company_name: 'dealerCompany',
|
||||
tier: 4,
|
||||
});
|
||||
const { account, admin } = await makeTestAccount(source, {
|
||||
tier: 5,
|
||||
company_name: 'company',
|
||||
parent_account_id: dealer.id,
|
||||
});
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
overrideAdB2cService(service, {
|
||||
getUsers: async () => {
|
||||
return [
|
||||
{
|
||||
id: admin.external_id,
|
||||
displayName: 'admin',
|
||||
identities: [
|
||||
{
|
||||
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
|
||||
issuer: 'issuer',
|
||||
issuerAssignedId: 'admin@example.com',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
},
|
||||
});
|
||||
overrideSendgridService(service, {});
|
||||
overrideBlobstorageService(service, {
|
||||
uploadImportsBlob: async () => {},
|
||||
});
|
||||
|
||||
// メール送信関数が呼ばれたかどうかで判定を行う。実際のメール送信は行わない。
|
||||
const spy = jest
|
||||
.spyOn(service['sendgridService'], 'sendMailWithU120')
|
||||
.mockImplementation();
|
||||
|
||||
const now = new Date();
|
||||
const date = `${now.getFullYear()}.${now.getMonth() + 1}.${now.getDate()}`;
|
||||
const filename = 'fileName.csv';
|
||||
|
||||
const users = [
|
||||
{
|
||||
name: 'name1',
|
||||
email: 'mail1@example.com',
|
||||
role: 1,
|
||||
authorId: 'HOGE1',
|
||||
autoRenew: 0,
|
||||
notification: 0,
|
||||
encryption: 0,
|
||||
encryptionPassword: 'string',
|
||||
prompt: 0,
|
||||
},
|
||||
{
|
||||
name: 'name2',
|
||||
email: 'mail2@example.com',
|
||||
role: 2,
|
||||
autoRenew: 0,
|
||||
notification: 0,
|
||||
},
|
||||
];
|
||||
await service.multipleImports(context, admin.external_id, filename, users);
|
||||
|
||||
// メール送信メソッドが呼ばれているか確認
|
||||
expect(spy).toHaveBeenCalledWith(
|
||||
context,
|
||||
['admin@example.com'],
|
||||
account.company_name,
|
||||
dealer.company_name,
|
||||
date,
|
||||
filename,
|
||||
);
|
||||
});
|
||||
|
||||
it('Blobアップロードに失敗した場合はエラーとなること', async () => {
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
|
||||
const { account: dealer } = await makeTestAccount(source, {
|
||||
company_name: 'dealerCompany',
|
||||
tier: 4,
|
||||
});
|
||||
const { account, admin } = await makeTestAccount(source, {
|
||||
tier: 5,
|
||||
company_name: 'company',
|
||||
parent_account_id: dealer.id,
|
||||
});
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
overrideAdB2cService(service, {
|
||||
getUsers: async () => {
|
||||
return [
|
||||
{
|
||||
id: admin.external_id,
|
||||
displayName: 'admin',
|
||||
identities: [
|
||||
{
|
||||
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
|
||||
issuer: 'issuer',
|
||||
issuerAssignedId: 'admin@example.com',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
},
|
||||
});
|
||||
overrideSendgridService(service, {});
|
||||
overrideBlobstorageService(service, {
|
||||
uploadImportsBlob: async () => {
|
||||
throw new Error('error');
|
||||
},
|
||||
});
|
||||
|
||||
const now = new Date();
|
||||
const date = `${now.getFullYear()}.${now.getMonth() + 1}.${now.getDate()}`;
|
||||
const filename = 'fileName.csv';
|
||||
|
||||
const users = [
|
||||
{
|
||||
name: 'name1',
|
||||
email: 'mail1@example.com',
|
||||
role: 1,
|
||||
authorId: 'HOGE1',
|
||||
autoRenew: 0,
|
||||
notification: 0,
|
||||
encryption: 0,
|
||||
encryptionPassword: 'string',
|
||||
prompt: 0,
|
||||
},
|
||||
{
|
||||
name: 'name2',
|
||||
email: 'mail2@example.com',
|
||||
role: 2,
|
||||
autoRenew: 0,
|
||||
notification: 0,
|
||||
},
|
||||
];
|
||||
|
||||
try {
|
||||
await service.multipleImports(
|
||||
context,
|
||||
admin.external_id,
|
||||
filename,
|
||||
users,
|
||||
);
|
||||
fail();
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('UsersService.multipleImportsComplate', () => {
|
||||
let source: DataSource | null = null;
|
||||
|
||||
|
||||
@ -24,6 +24,7 @@ import {
|
||||
import { UsersRepositoryService } from '../../repositories/users/users.repository.service';
|
||||
import { LicensesRepositoryService } from '../../repositories/licenses/licenses.repository.service';
|
||||
import {
|
||||
MultipleImportUser,
|
||||
GetRelationsResponse,
|
||||
MultipleImportErrors,
|
||||
User,
|
||||
@ -63,12 +64,11 @@ import { AccountNotFoundError } from '../../repositories/accounts/errors/types';
|
||||
import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils';
|
||||
import { AccountsRepositoryService } from '../../repositories/accounts/accounts.repository.service';
|
||||
import { Account } from '../../repositories/accounts/entity/account.entity';
|
||||
import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
private readonly logger = new Logger(UsersService.name);
|
||||
private readonly mailFrom: string;
|
||||
private readonly appDomain: string;
|
||||
constructor(
|
||||
private readonly accountsRepository: AccountsRepositoryService,
|
||||
private readonly usersRepository: UsersRepositoryService,
|
||||
@ -77,10 +77,8 @@ export class UsersService {
|
||||
private readonly adB2cService: AdB2cService,
|
||||
private readonly configService: ConfigService,
|
||||
private readonly sendgridService: SendGridService,
|
||||
) {
|
||||
this.mailFrom = this.configService.getOrThrow<string>('MAIL_FROM');
|
||||
this.appDomain = this.configService.getOrThrow<string>('APP_DOMAIN');
|
||||
}
|
||||
private readonly blobStorageService: BlobstorageService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Confirms user
|
||||
@ -1600,6 +1598,118 @@ export class UsersService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ユーザー一括登録用のファイルをBlobにアップロードする
|
||||
* @param context
|
||||
* @param externalId
|
||||
* @param fileName
|
||||
* @param users
|
||||
* @returns imports
|
||||
*/
|
||||
async multipleImports(
|
||||
context: Context,
|
||||
externalId: string,
|
||||
fileName: string,
|
||||
users: MultipleImportUser[],
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${
|
||||
this.multipleImports.name
|
||||
} | params: { externalId: ${externalId}, ` +
|
||||
`fileName: ${fileName}, ` +
|
||||
`users.length: ${users.length} };`,
|
||||
);
|
||||
try {
|
||||
// ユーザー情報を取得
|
||||
const user = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
|
||||
if (user == null) {
|
||||
throw new Error(`user not found. externalId=${externalId}`);
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
// 日時を生成(YYYYMMDD_HHMMSS)
|
||||
const dateTime =
|
||||
`${now.getFullYear().toString().padStart(4, '0')}` +
|
||||
`${(now.getMonth() + 1).toString().padStart(2, '0')}` + // 月は0から始まるため+1する
|
||||
`${now.getDate().toString().padStart(2, '0')}` +
|
||||
`_${now.getHours().toString().padStart(2, '0')}` +
|
||||
`${now.getMinutes().toString().padStart(2, '0')}` +
|
||||
`${now.getSeconds().toString().padStart(2, '0')}`;
|
||||
|
||||
// ファイル名を生成(U_YYYYMMDD_HHMMSS_アカウントID_ユーザーID.json)
|
||||
const jsonFileName = `U_${dateTime}_${user.account_id}_${user.id}.json`;
|
||||
|
||||
// ユーザー情報をJSON形式に変換
|
||||
const usersJson = JSON.stringify({
|
||||
account_id: user.account_id,
|
||||
user_id: user.id,
|
||||
user_role: user.role,
|
||||
external_id: user.external_id,
|
||||
file_name: fileName,
|
||||
date: Math.floor(now.getTime() / 1000),
|
||||
data: users,
|
||||
});
|
||||
|
||||
// Blobにファイルをアップロード(ユーザー一括登録用)
|
||||
await this.blobStorageService.uploadImportsBlob(
|
||||
context,
|
||||
jsonFileName,
|
||||
usersJson,
|
||||
);
|
||||
|
||||
// 受付完了メールを送信
|
||||
try {
|
||||
// アカウント・管理者情報を取得
|
||||
const { adminEmails, companyName } = await this.getAccountInformation(
|
||||
context,
|
||||
user.account_id,
|
||||
);
|
||||
|
||||
// Dealer情報を取得
|
||||
const dealer = await this.accountsRepository.findParentAccount(
|
||||
context,
|
||||
user.account_id,
|
||||
);
|
||||
const dealerName = dealer?.company_name ?? null;
|
||||
|
||||
const now = new Date();
|
||||
// 日時を生成(YYYY.MM.DD)
|
||||
const date = `${now.getFullYear()}.${
|
||||
now.getMonth() + 1 // 月は0から始まるため+1する
|
||||
}.${now.getDate()}`;
|
||||
|
||||
await this.sendgridService.sendMailWithU120(
|
||||
context,
|
||||
adminEmails,
|
||||
companyName,
|
||||
dealerName,
|
||||
date,
|
||||
fileName,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
// メール送信に関する例外はログだけ出して握りつぶす
|
||||
}
|
||||
} 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.multipleImports.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* アカウントIDを指定して、アカウント情報と管理者情報を取得する
|
||||
* @param context
|
||||
|
||||
@ -22,9 +22,11 @@ export class BlobstorageService {
|
||||
private readonly blobServiceClientUS: BlobServiceClient;
|
||||
private readonly blobServiceClientEU: BlobServiceClient;
|
||||
private readonly blobServiceClientAU: BlobServiceClient;
|
||||
private readonly blobServiceClientImports: BlobServiceClient;
|
||||
private readonly sharedKeyCredentialUS: StorageSharedKeyCredential;
|
||||
private readonly sharedKeyCredentialAU: StorageSharedKeyCredential;
|
||||
private readonly sharedKeyCredentialEU: StorageSharedKeyCredential;
|
||||
private readonly sharedKeyCredentialImports: StorageSharedKeyCredential;
|
||||
private readonly sasTokenExpireHour: number;
|
||||
constructor(private readonly configService: ConfigService) {
|
||||
this.sharedKeyCredentialUS = new StorageSharedKeyCredential(
|
||||
@ -39,6 +41,11 @@ export class BlobstorageService {
|
||||
this.configService.getOrThrow<string>('STORAGE_ACCOUNT_NAME_EU'),
|
||||
this.configService.getOrThrow<string>('STORAGE_ACCOUNT_KEY_EU'),
|
||||
);
|
||||
this.sharedKeyCredentialImports = new StorageSharedKeyCredential(
|
||||
this.configService.getOrThrow<string>('STORAGE_ACCOUNT_NAME_IMPORTS'),
|
||||
this.configService.getOrThrow<string>('STORAGE_ACCOUNT_KEY_IMPORTS'),
|
||||
);
|
||||
|
||||
this.blobServiceClientUS = new BlobServiceClient(
|
||||
this.configService.getOrThrow<string>('STORAGE_ACCOUNT_ENDPOINT_US'),
|
||||
this.sharedKeyCredentialUS,
|
||||
@ -51,6 +58,10 @@ export class BlobstorageService {
|
||||
this.configService.getOrThrow<string>('STORAGE_ACCOUNT_ENDPOINT_EU'),
|
||||
this.sharedKeyCredentialEU,
|
||||
);
|
||||
this.blobServiceClientImports = new BlobServiceClient(
|
||||
this.configService.getOrThrow<string>('STORAGE_ACCOUNT_ENDPOINT_IMPORTS'),
|
||||
this.sharedKeyCredentialImports,
|
||||
);
|
||||
this.sasTokenExpireHour = this.configService.getOrThrow<number>(
|
||||
'STORAGE_TOKEN_EXPIRE_TIME',
|
||||
);
|
||||
@ -457,6 +468,45 @@ export class BlobstorageService {
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 一括登録用のBlobを作成します
|
||||
* @param context
|
||||
* @param fileName
|
||||
* @param content
|
||||
* @returns imports blob
|
||||
*/
|
||||
async uploadImportsBlob(
|
||||
context: Context,
|
||||
fileName: string,
|
||||
content: string,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${
|
||||
this.uploadImportsBlob.name
|
||||
} | params: { fileName: ${fileName} };`,
|
||||
);
|
||||
|
||||
try {
|
||||
const containerClient =
|
||||
this.blobServiceClientImports.getContainerClient('import-users');
|
||||
const blockBlobClient = containerClient.getBlockBlobClient(fileName);
|
||||
const result = await blockBlobClient.upload(content, content.length);
|
||||
|
||||
if (result.errorCode) {
|
||||
throw new Error(
|
||||
`upload failed. errorCode: ${result.errorCode} fileName: ${fileName}`,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
throw e;
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.uploadImportsBlob.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets container client
|
||||
* @param companyName
|
||||
|
||||
@ -75,6 +75,10 @@ export class SendGridService {
|
||||
private readonly templateU119Text: string;
|
||||
private readonly templateU119NoParentHtml: string;
|
||||
private readonly templateU119NoParentText: string;
|
||||
private readonly templateU120Html: string;
|
||||
private readonly templateU120Text: string;
|
||||
private readonly templateU120NoParentHtml: string;
|
||||
private readonly templateU120NoParentText: string;
|
||||
private readonly templateU121Html: string;
|
||||
private readonly templateU121Text: string;
|
||||
private readonly templateU121NoParentHtml: string;
|
||||
@ -267,6 +271,25 @@ export class SendGridService {
|
||||
path.resolve(__dirname, `../../templates/template_U_119_no_parent.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU120Html = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_120.html`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU120Text = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_120.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU120NoParentHtml = readFileSync(
|
||||
path.resolve(
|
||||
__dirname,
|
||||
`../../templates/template_U_120_no_parent.html`,
|
||||
),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU120NoParentText = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_120_no_parent.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU121Html = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_121.html`),
|
||||
'utf-8',
|
||||
@ -1188,6 +1211,72 @@ export class SendGridService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* U-120のテンプレートを使用したメールを送信する
|
||||
* @param context
|
||||
* @param customerAdminMails アカウントの管理者(primary/secondary)のメールアドレス
|
||||
* @param customerAccountName アカウントの名前
|
||||
* @param dealerAccountName 問題発生時に問い合わせする先の上位のディーラー名(会社名)
|
||||
* @param tire1AdminMails 第一階層の管理者(primary/secondary)のメールアドレス
|
||||
* @returns mail with u119
|
||||
*/
|
||||
async sendMailWithU120(
|
||||
context: Context,
|
||||
customerAdminMails: string[],
|
||||
customerAccountName: string,
|
||||
dealerAccountName: string | null,
|
||||
requestTime: string,
|
||||
fileName: string,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU120.name}`,
|
||||
);
|
||||
try {
|
||||
const subject = 'ユーザー一括登録 受付通知 [U-120]';
|
||||
|
||||
let html: string;
|
||||
let text: string;
|
||||
console.log(dealerAccountName);
|
||||
|
||||
if (!dealerAccountName) {
|
||||
html = this.templateU120NoParentHtml
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(REQUEST_TIME, requestTime)
|
||||
.replaceAll(FILE_NAME, fileName);
|
||||
text = this.templateU120NoParentText
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(REQUEST_TIME, requestTime)
|
||||
.replaceAll(FILE_NAME, fileName);
|
||||
} else {
|
||||
html = this.templateU120Html
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(DEALER_NAME, dealerAccountName)
|
||||
.replaceAll(REQUEST_TIME, requestTime)
|
||||
.replaceAll(FILE_NAME, fileName);
|
||||
text = this.templateU120Text
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(DEALER_NAME, dealerAccountName)
|
||||
.replaceAll(REQUEST_TIME, requestTime)
|
||||
.replaceAll(FILE_NAME, fileName);
|
||||
}
|
||||
|
||||
// メールを送信する
|
||||
await this.sendMail(
|
||||
context,
|
||||
customerAdminMails,
|
||||
[],
|
||||
this.mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU120.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* U-121のテンプレートを使用したメールを送信する
|
||||
* @param context
|
||||
|
||||
@ -12,7 +12,6 @@ export const FILE_NAME = '$FILE_NAME$';
|
||||
export const TYPIST_NAME = '$TYPIST_NAME$';
|
||||
export const TEMPORARY_PASSWORD = '$TEMPORARY_PASSWORD$';
|
||||
export const REQUEST_TIME = '$REQUEST_TIME$';
|
||||
|
||||
export const EMAIL_DUPLICATION = `$EMAIL_DUPLICATION$`;
|
||||
export const AUTHOR_ID_DUPLICATION = `$AUTHOR_ID_DUPLICATION$`;
|
||||
export const UNEXPECTED_ERROR = `$UNEXPECTED_ERROR$`;
|
||||
|
||||
80
dictation_server/src/templates/template_U_120.html
Normal file
80
dictation_server/src/templates/template_U_120.html
Normal file
@ -0,0 +1,80 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>ユーザー一括登録 受付通知 [U-120]</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<h3><English></h3>
|
||||
<p>Dear $CUSTOMER_NAME$,</p>
|
||||
<p>ODMS Cloudをご利用いただきありがとうございます。</p>
|
||||
<p>
|
||||
CSVファイルによるユーザー一括登録を受け付けました。<br />
|
||||
- リクエスト日時:$REQUEST_TIME$<br />
|
||||
- SCVファイル名:$FILE_NAME$
|
||||
</p>
|
||||
<p>
|
||||
・登録完了には時間がかかる場合がありますので少々お待ちください。<br />
|
||||
・登録完了通知は別途お送りします。<br />
|
||||
・CSVファイルの内容に間違いがある場合は登録を完了できません。
|
||||
</p>
|
||||
<p>
|
||||
ディーラーによるサポートが必要な場合は、 $DEALER_NAME$
|
||||
にお問い合わせください。
|
||||
</p>
|
||||
<p>
|
||||
If you received this e-mail in error, please delete this e-mail from
|
||||
your system.<br />
|
||||
This is an automatically generated e-mail, please do not reply.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3><Deutsch></h3>
|
||||
<p>Dear $CUSTOMER_NAME$,</p>
|
||||
<p>ODMS Cloudをご利用いただきありがとうございます。</p>
|
||||
<p>
|
||||
CSVファイルによるユーザー一括登録を受け付けました。<br />
|
||||
- リクエスト日時:$REQUEST_TIME$<br />
|
||||
- SCVファイル名:$FILE_NAME$
|
||||
</p>
|
||||
<p>
|
||||
・登録完了には時間がかかる場合がありますので少々お待ちください。<br />
|
||||
・登録完了通知は別途お送りします。<br />
|
||||
・CSVファイルの内容に間違いがある場合は登録を完了できません。
|
||||
</p>
|
||||
<p>
|
||||
ディーラーによるサポートが必要な場合は、 $DEALER_NAME$
|
||||
にお問い合わせください。
|
||||
</p>
|
||||
<p>
|
||||
If you received this e-mail in error, please delete this e-mail from
|
||||
your system.<br />
|
||||
This is an automatically generated e-mail, please do not reply.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3><Français></h3>
|
||||
<p>Dear $CUSTOMER_NAME$,</p>
|
||||
<p>ODMS Cloudをご利用いただきありがとうございます。</p>
|
||||
<p>
|
||||
CSVファイルによるユーザー一括登録を受け付けました。<br />
|
||||
- リクエスト日時:$REQUEST_TIME$<br />
|
||||
- SCVファイル名:$FILE_NAME$
|
||||
</p>
|
||||
<p>
|
||||
・登録完了には時間がかかる場合がありますので少々お待ちください。<br />
|
||||
・登録完了通知は別途お送りします。<br />
|
||||
・CSVファイルの内容に間違いがある場合は登録を完了できません。
|
||||
</p>
|
||||
<p>
|
||||
ディーラーによるサポートが必要な場合は、 $DEALER_NAME$
|
||||
にお問い合わせください。
|
||||
</p>
|
||||
<p>
|
||||
If you received this e-mail in error, please delete this e-mail from
|
||||
your system.<br />
|
||||
This is an automatically generated e-mail, please do not reply.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
57
dictation_server/src/templates/template_U_120.txt
Normal file
57
dictation_server/src/templates/template_U_120.txt
Normal file
@ -0,0 +1,57 @@
|
||||
<English>
|
||||
|
||||
Dear $CUSTOMER_NAME$,
|
||||
|
||||
ODMS Cloudをご利用いただきありがとうございます。
|
||||
|
||||
CSVファイルによるユーザー一括登録を受け付けました。
|
||||
- リクエスト日時:$REQUEST_TIME$
|
||||
- SCVファイル名:$FILE_NAME$
|
||||
|
||||
・登録完了には時間がかかる場合がありますので少々お待ちください。
|
||||
・登録完了通知は別途お送りします。
|
||||
・CSVファイルの内容に間違いがある場合は登録を完了できません。
|
||||
|
||||
ディーラーによるサポートが必要な場合は、 $DEALER_NAME$ にお問い合わせください。
|
||||
|
||||
If you received this e-mail in error, please delete this e-mail from your system.
|
||||
This is an automatically generated e-mail, please do not reply.
|
||||
|
||||
|
||||
<Deutsch>
|
||||
|
||||
Dear $CUSTOMER_NAME$,
|
||||
|
||||
ODMS Cloudをご利用いただきありがとうございます。
|
||||
|
||||
CSVファイルによるユーザー一括登録を受け付けました。
|
||||
- リクエスト日時:$REQUEST_TIME$
|
||||
- SCVファイル名:$FILE_NAME$
|
||||
|
||||
・登録完了には時間がかかる場合がありますので少々お待ちください。
|
||||
・登録完了通知は別途お送りします。
|
||||
・CSVファイルの内容に間違いがある場合は登録を完了できません。
|
||||
|
||||
ディーラーによるサポートが必要な場合は、 $DEALER_NAME$ にお問い合わせください。
|
||||
|
||||
If you received this e-mail in error, please delete this e-mail from your system.
|
||||
This is an automatically generated e-mail, please do not reply.
|
||||
|
||||
<Français>
|
||||
|
||||
Dear $CUSTOMER_NAME$,
|
||||
|
||||
ODMS Cloudをご利用いただきありがとうございます。
|
||||
|
||||
CSVファイルによるユーザー一括登録を受け付けました。
|
||||
- リクエスト日時:$REQUEST_TIME$
|
||||
- SCVファイル名:$FILE_NAME$
|
||||
|
||||
・登録完了には時間がかかる場合がありますので少々お待ちください。
|
||||
・登録完了通知は別途お送りします。
|
||||
・CSVファイルの内容に間違いがある場合は登録を完了できません。
|
||||
|
||||
ディーラーによるサポートが必要な場合は、 $DEALER_NAME$ にお問い合わせください。
|
||||
|
||||
If you received this e-mail in error, please delete this e-mail from your system.
|
||||
This is an automatically generated e-mail, please do not reply.
|
||||
68
dictation_server/src/templates/template_U_120_no_parent.html
Normal file
68
dictation_server/src/templates/template_U_120_no_parent.html
Normal file
@ -0,0 +1,68 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>ユーザー一括登録 受付通知 [U-120]</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<h3><English></h3>
|
||||
<p>Dear $CUSTOMER_NAME$,</p>
|
||||
<p>ODMS Cloudをご利用いただきありがとうございます。</p>
|
||||
<p>
|
||||
CSVファイルによるユーザー一括登録を受け付けました。<br />
|
||||
- リクエスト日時:$REQUEST_TIME$<br />
|
||||
- SCVファイル名:$FILE_NAME$
|
||||
</p>
|
||||
<p>
|
||||
・登録完了には時間がかかる場合がありますので少々お待ちください。<br />
|
||||
・登録完了通知は別途お送りします。<br />
|
||||
・CSVファイルの内容に間違いがある場合は登録を完了できません。
|
||||
</p>
|
||||
<p>
|
||||
If you received this e-mail in error, please delete this e-mail from
|
||||
your system.<br />
|
||||
This is an automatically generated e-mail, please do not reply.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3><Deutsch></h3>
|
||||
<p>Dear $CUSTOMER_NAME$,</p>
|
||||
<p>ODMS Cloudをご利用いただきありがとうございます。</p>
|
||||
<p>
|
||||
CSVファイルによるユーザー一括登録を受け付けました。<br />
|
||||
- リクエスト日時:$REQUEST_TIME$<br />
|
||||
- SCVファイル名:$FILE_NAME$
|
||||
</p>
|
||||
<p>
|
||||
・登録完了には時間がかかる場合がありますので少々お待ちください。<br />
|
||||
・登録完了通知は別途お送りします。<br />
|
||||
・CSVファイルの内容に間違いがある場合は登録を完了できません。
|
||||
</p>
|
||||
<p>
|
||||
If you received this e-mail in error, please delete this e-mail from
|
||||
your system.<br />
|
||||
This is an automatically generated e-mail, please do not reply.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3><Français></h3>
|
||||
<p>Dear $CUSTOMER_NAME$,</p>
|
||||
<p>ODMS Cloudをご利用いただきありがとうございます。</p>
|
||||
<p>
|
||||
CSVファイルによるユーザー一括登録を受け付けました。<br />
|
||||
- リクエスト日時:$REQUEST_TIME$<br />
|
||||
- SCVファイル名:$FILE_NAME$
|
||||
</p>
|
||||
<p>
|
||||
・登録完了には時間がかかる場合がありますので少々お待ちください。<br />
|
||||
・登録完了通知は別途お送りします。<br />
|
||||
・CSVファイルの内容に間違いがある場合は登録を完了できません。
|
||||
</p>
|
||||
<p>
|
||||
If you received this e-mail in error, please delete this e-mail from
|
||||
your system.<br />
|
||||
This is an automatically generated e-mail, please do not reply.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
51
dictation_server/src/templates/template_U_120_no_parent.txt
Normal file
51
dictation_server/src/templates/template_U_120_no_parent.txt
Normal file
@ -0,0 +1,51 @@
|
||||
<English>
|
||||
|
||||
Dear $CUSTOMER_NAME$,
|
||||
|
||||
ODMS Cloudをご利用いただきありがとうございます。
|
||||
|
||||
CSVファイルによるユーザー一括登録を受け付けました。
|
||||
- リクエスト日時:$REQUEST_TIME$
|
||||
- SCVファイル名:$FILE_NAME$
|
||||
|
||||
・登録完了には時間がかかる場合がありますので少々お待ちください。
|
||||
・登録完了通知は別途お送りします。
|
||||
・CSVファイルの内容に間違いがある場合は登録を完了できません。
|
||||
|
||||
If you received this e-mail in error, please delete this e-mail from your system.
|
||||
This is an automatically generated e-mail, please do not reply.
|
||||
|
||||
|
||||
<Deutsch>
|
||||
|
||||
Dear $CUSTOMER_NAME$,
|
||||
|
||||
ODMS Cloudをご利用いただきありがとうございます。
|
||||
|
||||
CSVファイルによるユーザー一括登録を受け付けました。
|
||||
- リクエスト日時:$REQUEST_TIME$
|
||||
- SCVファイル名:$FILE_NAME$
|
||||
|
||||
・登録完了には時間がかかる場合がありますので少々お待ちください。
|
||||
・登録完了通知は別途お送りします。
|
||||
・CSVファイルの内容に間違いがある場合は登録を完了できません。
|
||||
|
||||
If you received this e-mail in error, please delete this e-mail from your system.
|
||||
This is an automatically generated e-mail, please do not reply.
|
||||
|
||||
<Français>
|
||||
|
||||
Dear $CUSTOMER_NAME$,
|
||||
|
||||
ODMS Cloudをご利用いただきありがとうございます。
|
||||
|
||||
CSVファイルによるユーザー一括登録を受け付けました。
|
||||
- リクエスト日時:$REQUEST_TIME$
|
||||
- SCVファイル名:$FILE_NAME$
|
||||
|
||||
・登録完了には時間がかかる場合がありますので少々お待ちください。
|
||||
・登録完了通知は別途お送りします。
|
||||
・CSVファイルの内容に間違いがある場合は登録を完了できません。
|
||||
|
||||
If you received this e-mail in error, please delete this e-mail from your system.
|
||||
This is an automatically generated e-mail, please do not reply.
|
||||
Loading…
x
Reference in New Issue
Block a user