Merged PR 484: NotificationHubsの通知フォーマット

## 概要
[Task2820: NotificationHubsの通知フォーマット](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2820)

- NotificationHubsで通知を行う処理を修正
  - 通知内容を変更
    - デスクトップ側でレンダリングを行うため、こちらからは必要なデータを送信する

- 通知処理を読んでいる箇所を修正
  - タスクキャンセル
  - チェックアウト候補変更
  - 音声ファイルアップロード完了(タスク作成)

## レビューポイント
- taskService内で通知をする箇所が複数あったのでプライベートメソッドとして切り出したが、fileServiceの通知処理もまとめたほうが良いか。
  - まとめる場合は、どこに切り出すか。
    - sendNotify(今回作成したプライベートメソッド)に、TasksRepositoryServiceやUserGroupsRepositoryServiceを引数に追加して共通関数としてCommonのどこかに配置する?
  - notificationhub.service.tsのnotifyメソッドの中で通知内容のための情報取得~通知内容作成まで全部行うようにする?

## 動作確認状況
- ローカルで確認

## 補足
- 相談、参考資料などがあれば
This commit is contained in:
saito.k 2023-11-15 02:33:03 +00:00
parent 36401d0542
commit ad49a19f04
12 changed files with 234 additions and 151 deletions

View File

@ -11,7 +11,7 @@
"dependencies": {
"@azure/identity": "^3.1.3",
"@azure/keyvault-secrets": "^4.6.0",
"@azure/notification-hubs": "^1.0.2",
"@azure/notification-hubs": "^1.0.3",
"@azure/storage-blob": "^12.14.0",
"@microsoft/microsoft-graph-client": "^3.0.5",
"@nestjs/axios": "^0.1.0",
@ -341,6 +341,22 @@
"openapi-types": ">=7"
}
},
"node_modules/@azure-rest/core-client": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-1.1.4.tgz",
"integrity": "sha512-RUIQOA8T0WcbNlddr8hjl2MuC5GVRqmMwPXqBVsgvdKesLy+eg3y/6nf3qe2fvcJMI1gF6VtgU5U4hRaR4w4ag==",
"dependencies": {
"@azure/abort-controller": "^1.1.0",
"@azure/core-auth": "^1.3.0",
"@azure/core-rest-pipeline": "^1.5.0",
"@azure/core-tracing": "^1.0.1",
"@azure/core-util": "^1.0.0",
"tslib": "^2.2.0"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@azure/abort-controller": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz",
@ -353,15 +369,16 @@
}
},
"node_modules/@azure/core-auth": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz",
"integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.5.0.tgz",
"integrity": "sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw==",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-util": "^1.1.0",
"tslib": "^2.2.0"
},
"engines": {
"node": ">=12.0.0"
"node": ">=14.0.0"
}
},
"node_modules/@azure/core-client": {
@ -443,11 +460,12 @@
}
},
"node_modules/@azure/core-lro": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.1.tgz",
"integrity": "sha512-JHQy/bA3NOz2WuzOi5zEk6n/TJdAropupxUT521JIJvW7EXV2YN2SFYZrf/2RHeD28QAClGdynYadZsbmP+nyQ==",
"version": "2.5.4",
"resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.4.tgz",
"integrity": "sha512-3GJiMVH7/10bulzOKGrrLeG/uCBH/9VtxqaMcB9lIqAeamI/xYQSHJL/KcsLDuH+yTjYpro/u6D/MuRe4dN70Q==",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-util": "^1.2.0",
"@azure/logger": "^1.0.0",
"tslib": "^2.2.0"
},
@ -467,23 +485,22 @@
}
},
"node_modules/@azure/core-rest-pipeline": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.10.2.tgz",
"integrity": "sha512-e3WzAsRKLor5EgK2bQqR1OY5D7VBqzORHtlqtygZZQGCYOIBsynqrZBa8MFD1Ue9r8TPtofOLditalnlQHS45Q==",
"version": "1.12.2",
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.12.2.tgz",
"integrity": "sha512-wLLJQdL4v1yoqYtEtjKNjf8pJ/G/BqVomAWxcKOR1KbZJyCEnCv04yks7Y1NhJ3JzxbDs307W67uX0JzklFdCg==",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-auth": "^1.4.0",
"@azure/core-tracing": "^1.0.1",
"@azure/core-util": "^1.0.0",
"@azure/core-util": "^1.3.0",
"@azure/logger": "^1.0.0",
"form-data": "^4.0.0",
"http-proxy-agent": "^5.0.0",
"https-proxy-agent": "^5.0.0",
"tslib": "^2.2.0",
"uuid": "^8.3.0"
"tslib": "^2.2.0"
},
"engines": {
"node": ">=14.0.0"
"node": ">=16.0.0"
}
},
"node_modules/@azure/core-tracing": {
@ -498,23 +515,23 @@
}
},
"node_modules/@azure/core-util": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.3.2.tgz",
"integrity": "sha512-2bECOUh88RvL1pMZTcc6OzfobBeWDBf5oBbhjIhT1MV9otMVWCzpOJkkiKtrnO88y5GGBelgY8At73KGAdbkeQ==",
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.6.1.tgz",
"integrity": "sha512-h5taHeySlsV9qxuK64KZxy4iln1BtMYlNt5jbuEFN3UFSAd1EwKg/Gjl5a6tZ/W8t6li3xPnutOx7zbDyXnPmQ==",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"tslib": "^2.2.0"
},
"engines": {
"node": ">=14.0.0"
"node": ">=16.0.0"
}
},
"node_modules/@azure/core-xml": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@azure/core-xml/-/core-xml-1.3.3.tgz",
"integrity": "sha512-Go/xGz7nGqVINsD9O7gOfe8uiR1S+IFcw9WTUPJHSzoFT6F5ZWjXIIlSikLZm77TtmxzXGnQYjjiZIoIZ4x14A==",
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/@azure/core-xml/-/core-xml-1.3.4.tgz",
"integrity": "sha512-B1xI79Ur/u+KR69fGTcsMNj8KDjBSqAy0Ys6Byy4Qm1CqoUy7gCT5A7Pej0EBWRskuH6bpCwrAnosfmQEalkcg==",
"dependencies": {
"fast-xml-parser": "^4.0.8",
"fast-xml-parser": "^4.2.4",
"tslib": "^2.2.0"
},
"engines": {
@ -628,25 +645,31 @@
}
},
"node_modules/@azure/notification-hubs": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@azure/notification-hubs/-/notification-hubs-1.0.2.tgz",
"integrity": "sha512-INtgq8uFQpncwbKm4It8M0GkKIePNDNybhuXs4cQPf5H0i9CbfFEt2c6LtT1AdEzbWfUhjsmU5y0p3YDmecwwg==",
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@azure/notification-hubs/-/notification-hubs-1.0.3.tgz",
"integrity": "sha512-IWzIO2nKwM+9CWgnTsMyZhXxR7q3n3/aXpJiqUn1U3xHXTtHVE+joZ/AL9fCZTEBVB1PQvbrjp8VZvwEptqJ/Q==",
"dependencies": {
"@azure-rest/core-client": "^1.1.4",
"@azure/abort-controller": "^1.1.0",
"@azure/core-client": "^1.6.1",
"@azure/core-lro": "^2.4.0",
"@azure/core-paging": "^1.3.0",
"@azure/core-rest-pipeline": "^1.8.1",
"@azure/core-auth": "^1.5.0",
"@azure/core-lro": "^2.5.4",
"@azure/core-paging": "^1.5.0",
"@azure/core-rest-pipeline": "^1.12.2",
"@azure/core-tracing": "^1.0.1",
"@azure/core-util": "^1.3.0",
"@azure/core-xml": "^1.3.1",
"@azure/logger": "^1.0.3",
"tslib": "^2.4.0"
"@azure/core-util": "^1.6.1",
"@azure/core-xml": "^1.3.4",
"@azure/logger": "^1.0.4",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=14.0.0"
"node": ">=18.0.0"
}
},
"node_modules/@azure/notification-hubs/node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
},
"node_modules/@azure/storage-blob": {
"version": "12.14.0",
"resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.14.0.tgz",
@ -5899,17 +5922,17 @@
"integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="
},
"node_modules/fast-xml-parser": {
"version": "4.2.6",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.6.tgz",
"integrity": "sha512-Xo1qV++h/Y3Ng8dphjahnYe+rGHaaNdsYOBWL9Y9GCPKpNKilJtilvWkLcI9f9X2DoKTLsZsGYAls5+JL5jfLA==",
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.2.tgz",
"integrity": "sha512-rmrXUXwbJedoXkStenj1kkljNF7ugn5ZjR9FJcwmCfcCbtOMDghPajbc+Tck6vE6F5XsDmx+Pr2le9fw8+pXBg==",
"funding": [
{
"type": "paypal",
"url": "https://paypal.me/naturalintelligence"
},
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
},
{
"type": "paypal",
"url": "https://paypal.me/naturalintelligence"
}
],
"dependencies": {

View File

@ -31,7 +31,7 @@
"dependencies": {
"@azure/identity": "^3.1.3",
"@azure/keyvault-secrets": "^4.6.0",
"@azure/notification-hubs": "^1.0.2",
"@azure/notification-hubs": "^1.0.3",
"@azure/storage-blob": "^12.14.0",
"@microsoft/microsoft-graph-client": "^3.0.5",
"@nestjs/axios": "^0.1.0",

View File

@ -1,13 +0,0 @@
/*
M+6
- 1~2...
- 3~4...
- 5~6
ex)
E00XXXX : 正常系メッセージ
E01XXXX : 以上系メッセージ
EXX00XX : 全般
EXX01XX : タスク関連
*/
export const NotifyMessageCodes = ['M000101'] as const;

View File

@ -1,7 +0,0 @@
import { notifyMessages } from './message';
import { NotifyMessageCodeType } from './types/types';
export const makeNotifyMessage = (code: NotifyMessageCodeType): string => {
const msg = notifyMessages[code];
return msg;
};

View File

@ -1,6 +0,0 @@
import { NotifyMessages } from './types/types';
// エラーコードとメッセージ対応表
export const notifyMessages: NotifyMessages = {
M000101: 'You are assigned to Typist.',
};

View File

@ -1,7 +1,6 @@
import { NotifyMessageCodes } from '../code';
export type NotifyMessageCodeType = (typeof NotifyMessageCodes)[number];
export type NotifyMessages = {
[P in NotifyMessageCodeType]: string;
export type NotificationBody = {
filename: string;
authorId: string;
priority: string;
uploadedAt: string;
};

View File

@ -32,7 +32,6 @@ import {
import { createWorktype } from '../accounts/test/utility';
import { TasksRepositoryService } from '../../repositories/tasks/tasks.repository.service';
import { NotificationhubService } from '../../gateways/notificationhub/notificationhub.service';
import { makeNotifyMessage } from '../../common/notify/makeNotifyMessage';
import { getCheckoutPermissions, getTask } from '../tasks/test/utility';
import { DateWithZeroTime } from '../licenses/types/types';
import { LICENSE_ALLOCATED_STATUS, LICENSE_TYPE } from '../../constants';
@ -371,7 +370,12 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
expect(NotificationHubService.notify).toHaveBeenCalledWith(
makeContext('trackingId'),
[`user_${typistUserId}`],
makeNotifyMessage('M000101'),
{
authorId: 'AUTHOR_ID',
filename: 'file',
priority: 'High',
uploadedAt: '2023-05-26T11:22:33.444',
},
);
// 作成したタスクを取得
const resultTask = await getTaskFromJobNumber(source, result.jobNumber);
@ -467,7 +471,12 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
expect(NotificationHubService.notify).toHaveBeenCalledWith(
makeContext('trackingId'),
[`user_${typistUserId}`],
makeNotifyMessage('M000101'),
{
authorId: 'AUTHOR_ID',
filename: 'file',
priority: 'High',
uploadedAt: '2023-05-26T11:22:33.444',
},
);
// 作成したタスクを取得
const resultTask = await getTaskFromJobNumber(source, result.jobNumber);
@ -585,7 +594,12 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
expect(NotificationHubService.notify).toHaveBeenCalledWith(
makeContext('trackingId'),
[`user_${typistUserId}`],
makeNotifyMessage('M000101'),
{
authorId: 'AUTHOR_ID',
filename: 'file',
priority: 'High',
uploadedAt: '2023-05-26T11:22:33.444',
},
);
// 作成したタスクを取得
const resultTask = await getTaskFromJobNumber(source, result.jobNumber);
@ -702,7 +716,12 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
expect(NotificationHubService.notify).toHaveBeenCalledWith(
makeContext('trackingId'),
[`user_${typistUserId}`],
makeNotifyMessage('M000101'),
{
authorId: 'XXXXXXXXXX',
filename: 'file',
priority: 'High',
uploadedAt: '2023-05-26T11:22:33.444',
},
);
// 作成したタスクを取得
const resultTask = await getTaskFromJobNumber(source, result.jobNumber);
@ -716,7 +735,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
// タスクのチェックアウト権限が想定通りワークフローで設定されているのユーザーIDで作成されているか確認
expect(resultCheckoutPermission.length).toEqual(1);
expect(resultCheckoutPermission[0].user_group_id).toEqual(userGroupId);
}, 1000000);
});
it('ワークフローが見つからない場合、タスク作成時に、自動ルーティングを行うことができない', async () => {
if (!source) fail();

View File

@ -30,7 +30,6 @@ import {
} from '../../repositories/accounts/errors/types';
import { Task } from '../../repositories/tasks/entity/task.entity';
import { UserGroupsRepositoryService } from '../../repositories/user_groups/user_groups.repository.service';
import { makeNotifyMessage } from '../../common/notify/makeNotifyMessage';
import { NotificationhubService } from '../../gateways/notificationhub/notificationhub.service';
import {
LicenseExpiredError,
@ -237,11 +236,12 @@ export class FilesService {
this.logger.log(`tags: ${tags}`);
// タグ対象に通知送信
await this.notificationhubService.notify(
context,
tags,
makeNotifyMessage('M000101'),
);
await this.notificationhubService.notify(context, tags, {
authorId: authorId,
filename: fileName.replace('.zip', ''),
priority: priority === '00' ? 'Normal' : 'High',
uploadedAt: uploadedDate,
});
// 追加したタスクのJOBナンバーを返却
return { jobNumber: task.job_number };

View File

@ -35,7 +35,6 @@ import {
} from '../workflows/test/utility';
import { createTemplateFile } from '../templates/test/utility';
import { NotificationhubService } from '../../gateways/notificationhub/notificationhub.service';
import { makeNotifyMessage } from '../../common/notify/makeNotifyMessage';
import { Roles } from '../../common/types/role';
describe('TasksService', () => {
@ -835,6 +834,9 @@ describe('changeCheckoutPermission', () => {
await createCheckoutPermissions(source, taskId, typistUserId_1);
await createCheckoutPermissions(source, taskId, undefined, userGroupId);
const service = module.get<TasksService>(TasksService);
const NotificationHubService = module.get<NotificationhubService>(
NotificationhubService,
);
await service.changeCheckoutPermission(
makeContext('trackingId'),
1,
@ -850,6 +852,18 @@ describe('changeCheckoutPermission', () => {
user_id: typistUserId_2,
user_group_id: null,
});
const resultTask = await getTask(source, taskId);
// 通知処理が想定通りの引数で呼ばれているか確認
expect(NotificationHubService.notify).toHaveBeenCalledWith(
makeContext('trackingId'),
[`user_${typistUserId_2}`],
{
authorId: 'MY_AUTHOR_ID',
filename: 'x',
priority: 'High',
uploadedAt: resultTask?.file?.uploaded_at.toISOString(),
},
);
});
it('タスクのチェックアウト権限を変更できる。(グループ指定)', async () => {
@ -903,6 +917,9 @@ describe('changeCheckoutPermission', () => {
await createCheckoutPermissions(source, taskId, typistUserId_1);
await createCheckoutPermissions(source, taskId, undefined, userGroupId_1);
const service = module.get<TasksService>(TasksService);
const NotificationHubService = module.get<NotificationhubService>(
NotificationhubService,
);
await service.changeCheckoutPermission(
makeContext('trackingId'),
1,
@ -918,6 +935,19 @@ describe('changeCheckoutPermission', () => {
user_id: null,
user_group_id: userGroupId_2,
});
const resultTask = await getTask(source, taskId);
// 通知処理が想定通りの引数で呼ばれているか確認
expect(NotificationHubService.notify).toHaveBeenCalledWith(
makeContext('trackingId'),
[`user_${typistUserId_2}`],
{
authorId: 'MY_AUTHOR_ID',
filename: 'x',
priority: 'High',
uploadedAt: resultTask?.file?.uploaded_at.toISOString(),
},
);
});
it('タスクのチェックアウト権限を変更できる。(チェックアウト権限を外す)', async () => {
@ -2655,9 +2685,14 @@ describe('cancel', () => {
expect(NotificationHubService.notify).toHaveBeenCalledWith(
makeContext('trackingId'),
[`user_${typistUserId}`],
makeNotifyMessage('M000101'),
{
authorId: 'AUTHOR_ID',
filename: 'x',
priority: 'High',
uploadedAt: resultTask?.file?.uploaded_at.toISOString(),
},
);
}, 1000000);
});
it('API実行者のRoleがAdminの場合、自身が文字起こし実行中のタスクをキャンセルし、そのタスクの自動ルーティングを行うAPI実行者のAuthorIDと音声ファイルに紐づくWorkType', async () => {
if (!source) fail();
@ -2760,7 +2795,12 @@ describe('cancel', () => {
expect(NotificationHubService.notify).toHaveBeenCalledWith(
makeContext('trackingId'),
[`user_${autoRoutingTypistUserId}`],
makeNotifyMessage('M000101'),
{
authorId: 'AUTHOR_ID',
filename: 'x',
priority: 'High',
uploadedAt: resultTask?.file?.uploaded_at.toISOString(),
},
);
});
it('API実行者のRoleがTypistの場合、自身が文字起こし実行中のタスクをキャンセルするが、一致するワークフローがない場合は自動ルーティングを行うことができない', async () => {

View File

@ -31,7 +31,6 @@ import { Roles } from '../../common/types/role';
import { InvalidRoleError } from './errors/types';
import { NotificationhubService } from '../../gateways/notificationhub/notificationhub.service';
import { UserGroupsRepositoryService } from '../../repositories/user_groups/user_groups.repository.service';
import { makeNotifyMessage } from '../../common/notify/makeNotifyMessage';
import { Context } from '../../common/log';
import { User } from '../../repositories/users/entity/user.entity';
@ -444,31 +443,13 @@ export class TasksService {
user.author_id ?? undefined,
);
const groupMembers =
await this.userGroupsRepositoryService.getGroupMembersFromGroupIds(
typistGroupIds,
);
// 重複のない割り当て候補ユーザーID一覧を取得する
const distinctUserIds = [
...new Set([...typistIds, ...groupMembers.map((x) => x.user_id)]),
];
// 割り当てられたユーザーがいない場合は通知不要
if (distinctUserIds.length === 0) {
this.logger.log('No user assigned.');
return;
}
// タグを生成
const tags = distinctUserIds.map((x) => `user_${x}`);
this.logger.log(`tags: ${tags}`);
// タグ対象に通知送信
await this.notificationhubService.notify(
// 通知を送信する
await this.sendNotify(
context,
tags,
makeNotifyMessage('M000101'),
typistIds,
typistGroupIds,
audioFileId,
user.account_id,
);
} catch (e) {
// 処理の本筋はタスクキャンセルのため自動ルーティングに失敗してもエラーにしない
@ -600,35 +581,13 @@ export class TasksService {
.flatMap((assignee) =>
assignee.typistUserId ? [assignee.typistUserId] : [],
);
const groupMembers =
await this.userGroupsRepositoryService.getGroupMembersFromGroupIds(
assigneesGroupIds,
);
// 重複のない割り当て候補ユーザーID一覧を取得する
const distinctUserIds = [
...new Set([
...assigneesUserIds,
...groupMembers.map((x) => x.user_id),
]),
];
// 割り当てられたユーザーがいない場合は通知不要
if (distinctUserIds.length === 0) {
this.logger.log('No user assigned.');
return;
}
// タグを生成
const tags = distinctUserIds.map((x) => `user_${x}`);
this.logger.log(`tags: ${tags}`);
// タグ対象に通知送信
await this.notificationhubService.notify(
// 通知を送信する
await this.sendNotify(
context,
tags,
makeNotifyMessage('M000101'),
assigneesUserIds,
assigneesGroupIds,
audioFileId,
account_id,
);
} catch (e) {
this.logger.error(`error=${e}`);
@ -662,4 +621,51 @@ export class TasksService {
);
}
}
// 通知を送信するプライベートメソッド
private async sendNotify(
context: Context,
typistUserIds: number[],
typistGroupIds: number[],
audioFileId: number,
accountId: number,
): Promise<void> {
const groupMembers =
await this.userGroupsRepositoryService.getGroupMembersFromGroupIds(
typistGroupIds,
);
// 重複のない割り当て候補ユーザーID一覧を取得する
const distinctUserIds = [
...new Set([...typistUserIds, ...groupMembers.map((x) => x.user_id)]),
];
// 割り当てられたユーザーがいない場合は通知不要
if (distinctUserIds.length === 0) {
this.logger.log('No user assigned.');
return;
}
// タグを生成
const tags = distinctUserIds.map((x) => `user_${x}`);
this.logger.log(`tags: ${tags}`);
// 通知内容に含む音声ファイル情報を取得
const { file } = await this.taskRepository.getTaskAndAudioFile(
audioFileId,
accountId,
[TASK_STATUS.UPLOADED],
);
if (!file) {
throw new Error('audio file not found');
}
// タグ対象に通知送信
await this.notificationhubService.notify(context, tags, {
authorId: file.author_id,
filename: file.file_name.replace('.zip', ''),
priority: file.priority === '00' ? 'Normal' : 'High',
uploadedAt: file.uploaded_at.toISOString(),
});
}
}

View File

@ -207,6 +207,9 @@ export const getTask = async (
task_id: number,
): Promise<Task | null> => {
const task = await datasource.getRepository(Task).findOne({
relations: {
file: true,
},
where: {
id: task_id,
},

View File

@ -5,13 +5,15 @@ import {
createAppleNotificationBody,
createAppleNotification,
createTagExpression,
createWindowsToastNotification,
createWindowsInstallation,
createAppleInstallation,
createWindowsRawNotification,
} from '@azure/notification-hubs';
import { TAG_MAX_COUNT } from '../../constants';
import { PNS } from '../../constants';
import { Context } from '../../common/log';
import { NotificationBody } from '../../common/notify/types/types';
@Injectable()
export class NotificationhubService {
private readonly logger = new Logger(NotificationhubService.name);
@ -76,17 +78,22 @@ export class NotificationhubService {
/**
*
* @param context
* @param tags
* @param message
* @param bodyContent
* @returns notify
*/
async notify(
context: Context,
tags: string[],
message: string,
bodyContent: NotificationBody,
): Promise<void> {
this.logger.log(
`[IN] [${context.trackingId}] ${this.notify.name} | params: { tags: ${tags}, message: ${message} }`,
`[IN] [${context.trackingId}] ${
this.notify.name
} | params: { tags: ${tags}, bodyContent: ${JSON.stringify(
bodyContent,
)} }`,
);
try {
@ -99,8 +106,15 @@ export class NotificationhubService {
// Windows
try {
const body = `<toast><visual><binding template="ToastText01"><text id="1">${message}</text></binding></visual></toast>`;
const notification = createWindowsToastNotification({ body });
const body = {
wns: {
alert: '',
},
newDictation: bodyContent,
};
const notification = createWindowsRawNotification({
body: JSON.stringify(body),
});
const result = await this.client.sendNotification(notification, {
tagExpression,
});
@ -110,7 +124,12 @@ export class NotificationhubService {
}
// Apple
try {
const body = createAppleNotificationBody({ aps: { alert: message } });
const body = createAppleNotificationBody({
aps: {
alert: '',
},
newDictation: bodyContent,
});
const notification = createAppleNotification({ body });
const result = await this.client.sendNotification(notification, {
tagExpression,