Merged PR 893: API修正(upload-finished)

## 概要
[Task4033: API修正(upload-finished)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/4033)

- JobNumberテーブルから取得したJOBNUMBERを使用してタスクを作成する。
- テスト追加

## レビューポイント
- テストケースは足りているか
- JOBNUMBERの採番ロジックに誤りはないか

## クエリの変更
- Repositoryを変更し、クエリが変更された場合は変更内容を確認する
-  JobNumberテーブルからの取得と更新クエリを追加した
  - 既存のクエリを変更はしていない

## 動作確認状況
- ローカルで確認
- 行った修正がデグレを発生させていないことを確認できるか
  - 具体的にどのような確認をしたか
    - テストケースを修正し、既存テストがすべて通ることを確認

## 補足
- 相談、参考資料などがあれば
This commit is contained in:
saito.k 2024-05-13 05:04:16 +00:00
parent 228e21ba78
commit 35e2d626a0
10 changed files with 611 additions and 25 deletions

View File

@ -9,6 +9,8 @@ import {
} from '../../constants';
import { License } from '../../repositories/licenses/entity/license.entity';
import { AccountArchive } from '../../repositories/accounts/entity/account_archive.entity';
import { Task } from '../../repositories/tasks/entity/task.entity';
import { JobNumber } from '../../repositories/job_number/entity/job_number.entity';
type InitialTestDBState = {
tier1Accounts: { account: Account; users: User[] }[];
@ -421,3 +423,26 @@ export const getLicenses = async (
});
return licenses;
};
export const getTasks = async (
datasource: DataSource,
account_id: number,
): Promise<Task[]> => {
const tasks = await datasource.getRepository(Task).find({
where: { account_id: account_id },
});
return tasks;
};
// job_numberを取得する
export const getJobNumber = async (
datasource: DataSource,
account_id: number,
): Promise<JobNumber | null> => {
const jobNumber = await datasource.getRepository(JobNumber).findOne({
where: {
account_id: account_id,
},
});
return jobNumber;
};

View File

@ -41,6 +41,7 @@ import {
getLicenses,
getUserArchive,
getAccountArchive,
getTasks,
} from '../../common/test/utility';
import { AccountsService } from './accounts.service';
import { Context, makeContext } from '../../common/log';
@ -94,7 +95,6 @@ import { truncateAllTable } from '../../common/test/init';
import {
createTask,
getCheckoutPermissions,
getTasks,
} from '../tasks/test/utility';
import { createCheckoutPermissions } from '../tasks/test/utility';
import { TestLogger } from '../../common/test/logger';

View File

@ -3,6 +3,7 @@ import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import { makeBlobstorageServiceMockValue } from './test/files.service.mock';
import { DataSource } from 'typeorm';
import {
createJobNumber,
createLicense,
createTask,
createUserGroupAndMember,
@ -13,6 +14,8 @@ import {
import { FilesService } from './files.service';
import { makeContext } from '../../common/log';
import {
getJobNumber,
getTasks,
makeHierarchicalAccounts,
makeTestAccount,
makeTestSimpleAccount,
@ -50,6 +53,7 @@ import {
} from '../../constants';
import { truncateAllTable } from '../../common/test/init';
import { TestLogger } from '../../common/test/logger';
import { TasksService } from '../tasks/tasks.service';
describe('publishUploadSas', () => {
let source: DataSource | null = null;
@ -322,6 +326,10 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
);
// ワークフロータイピストを作成
await createWorkflowTypist(source, workflowId, typistUserId);
// 初期値のジョブナンバーでjob_numberテーブルを作成
await createJobNumber(source, accountId, '00000000');
const blobParam = makeBlobstorageServiceMockValue();
const notificationParam = makeDefaultNotificationhubServiceMockValue();
@ -335,6 +343,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
const notificationHubService = module.get<NotificationhubService>(
NotificationhubService,
);
const result = await service.uploadFinished(
makeContext('trackingId', 'requestId'),
authorExternalId,
@ -404,7 +413,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
accountId,
'http://blob/url/file.zip',
'file.zip',
'01',
'Uploaded',
typistUserId,
authorAuthorId ?? '',
);
@ -423,6 +432,10 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
);
// ワークフロータイピストを作成
await createWorkflowTypist(source, workflowId, typistUserId);
// 最新のジョブナンバーでjob_numberテーブルを作成
await createJobNumber(source, accountId, '00000001');
const blobParam = makeBlobstorageServiceMockValue();
const notificationParam = makeDefaultNotificationhubServiceMockValue();
@ -546,6 +559,9 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
userGroupId, // ルーティング先のユーザーグループIDを設定
);
// 初期値のジョブナンバーでjob_numberテーブルを作成
await createJobNumber(source, accountId, '00000000');
const blobParam = makeBlobstorageServiceMockValue();
const notificationParam = makeDefaultNotificationhubServiceMockValue();
@ -668,6 +684,9 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
userGroupId, // ルーティング先のユーザーグループIDを設定
);
// 初期値のジョブナンバーでjob_numberテーブルを作成
await createJobNumber(source, accountId, '00000000');
const blobParam = makeBlobstorageServiceMockValue();
const notificationParam = makeDefaultNotificationhubServiceMockValue();
@ -736,6 +755,10 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
role: 'author',
author_id: 'AUTHOR_ID',
});
// 初期値のジョブナンバーでjob_numberテーブルを作成
await createJobNumber(source, accountId, '00000000');
const blobParam = makeBlobstorageServiceMockValue();
const notificationParam = makeDefaultNotificationhubServiceMockValue();
@ -835,6 +858,9 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
);
}
// 最新のジョブナンバーでjob_numberテーブルを作成
await createJobNumber(source, accountId, '00000003');
const service = module.get<FilesService>(FilesService);
const spy = jest
.spyOn(service['sendGridService'], 'sendMail')
@ -918,6 +944,9 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
);
}
// 最新のジョブナンバーでjob_numberテーブルを作成
await createJobNumber(source, accountId, '00000003');
const service = module.get<FilesService>(FilesService);
// メール送信関数が呼ばれたかどうかで判定を行う。実際のメール送信は行わない。
const spy = jest
@ -1004,6 +1033,9 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
);
}
// 最新のジョブナンバーでjob_numberテーブルを作成
await createJobNumber(source, accountId, '00000004');
const service = module.get<FilesService>(FilesService);
// メール送信関数が呼ばれたかどうかで判定を行う。実際のメール送信は行わない。
const spy = jest
@ -1095,6 +1127,9 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
);
}
// 最新のジョブナンバーでjob_numberテーブルを作成
await createJobNumber(source, accountId, '00000004');
const service = module.get<FilesService>(FilesService);
// メール送信関数が呼ばれたかどうかで判定を行う。実際のメール送信は行わない。
const spy = jest
@ -1361,7 +1396,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
accountId,
`http://blob/url/file_${i}.zip`,
`file_${i}.zip`,
'01',
'Uploaded',
typistUserId,
authorAuthorId ?? '',
undefined,
@ -1381,6 +1416,9 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
false,
);
// 最新のジョブナンバーでjob_numberテーブルを作成
await createJobNumber(source, accountId, '00000010');
const module = await makeTestingModuleWithBlobAndNotification(
source,
blobParam,
@ -1487,7 +1525,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
accountId,
`http://blob/url/file_${i}.zip`,
`file_${i}.zip`,
'01',
'Uploaded',
typistUserId,
authorAuthorId ?? '',
undefined,
@ -1496,6 +1534,9 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
);
}
// 最新のジョブナンバーでjob_numberテーブルを作成
await createJobNumber(source, accountId, '00000010');
const module = await makeTestingModuleWithBlobAndNotification(
source,
blobParam,
@ -1621,6 +1662,8 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
'Backup',
false,
);
// 最新のジョブナンバーでjob_numberテーブルを作成
await createJobNumber(source, accountId, '00000010');
const module = await makeTestingModuleWithBlobAndNotification(
source,
@ -1747,6 +1790,8 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
'Backup',
false,
);
// 最新のジョブナンバーでjob_numberテーブルを作成
await createJobNumber(source, accountId, '99999999');
const module = await makeTestingModuleWithBlobAndNotification(
source,
@ -1844,6 +1889,10 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
);
// ワークフロータイピストを作成
await createWorkflowTypist(source, workflowId, typistUserId);
// 最新のジョブナンバーでjob_numberテーブルを作成
await createJobNumber(source, accountId, '99999999');
const blobParam = makeBlobstorageServiceMockValue();
const notificationParam = makeDefaultNotificationhubServiceMockValue();
@ -1915,6 +1964,457 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
expect(resultCheckoutPermission.length).toEqual(1);
expect(resultCheckoutPermission[0].user_id).toEqual(typistUserId);
});
it('タスク作成時に採番されるジョブナンバーが常に最新タスクのジョブナンバー + 1となる(最新タスクが削除されている場合)', async () => {
if (!source) fail();
const { id: accountId } = await makeTestSimpleAccount(source);
const {
external_id: authorExternalId,
id: authorUserId,
author_id: authorAuthorId,
} = await makeTestUser(source, {
account_id: accountId,
external_id: 'author-user-external-id',
role: 'author',
author_id: 'AUTHOR_ID',
});
const { id: typistUserId } = await makeTestUser(source, {
account_id: accountId,
external_id: 'typist-user-external-id',
role: 'typist',
author_id: undefined,
});
// ワークタイプを作成
const { id: worktypeId, custom_worktype_id } = await createWorktype(
source,
accountId,
'worktypeId',
);
// テンプレートファイルを作成
const { id: templateFileId } = await createTemplateFile(
source,
accountId,
'templateFile',
'http://blob/url/templateFile.zip',
);
// ワークフローを作成
const { id: workflowId } = await createWorkflow(
source,
accountId,
authorUserId,
worktypeId,
templateFileId,
);
// ワークフロータイピストを作成
await createWorkflowTypist(source, workflowId, typistUserId);
const blobParam = makeBlobstorageServiceMockValue();
const notificationParam = makeDefaultNotificationhubServiceMockValue();
// タスクを10件作成, ジョブナンバーは00000001から00000010まで
for (let i = 1; i <= 10; i++) {
await createTask(
source,
accountId,
`http://blob/url/file_${i}.zip`,
`file_${i}.zip`,
'Uploaded',
typistUserId,
authorAuthorId ?? '',
undefined,
undefined,
i.toString().padStart(8, '0'),
);
}
// 最新のジョブナンバーでjob_numberテーブルを作成
await createJobNumber(source, accountId, '00000010');
{
// 初期データ確認
const tasks = await getTasks(source, accountId);
expect(tasks.length).toEqual(10);
const jobNumber = await getJobNumber(source, accountId);
expect(jobNumber?.job_number).toEqual('00000010');
}
const module = await makeTestingModuleWithBlobAndNotification(
source,
blobParam,
notificationParam,
);
if (!module) fail();
const service = module.get<FilesService>(FilesService);
const taskService = module.get<TasksService>(TasksService);
const notificationHubService = module.get<NotificationhubService>(
NotificationhubService,
);
// 最新のジョブナンバーのタスクを取得
const latestTask = await getTaskFromJobNumber(source, '00000010');
await taskService.deleteTask(
makeContext('trackingId', 'requestId'),
authorExternalId,
latestTask?.audio_file_id ?? 0, // 最新タスクのaudioFileId
);
{
// タスク削除確認
const tasks = await getTasks(source, accountId);
expect(tasks.length).toEqual(9);
// JobNumberが00000010のタスクが存在しないことを確認
expect(
tasks.find((task) => task.job_number === '00000010'),
).toBeUndefined();
}
const result = await service.uploadFinished(
makeContext('trackingId', 'requestId'),
authorExternalId,
'http://blob/url/file.zip',
authorAuthorId ?? '',
'file.zip',
'11:22:33',
'2023-05-26T11:22:33.444',
'2023-05-26T11:22:33.444',
'2023-05-26T11:22:33.444',
256,
'01',
'DS2',
'comment',
custom_worktype_id,
optionItemList,
false,
);
expect(result.jobNumber).toEqual('00000011');
// job_numberテーブルが正しく更新されているか確認
const jobNumber = await getJobNumber(source, accountId);
expect(jobNumber?.job_number).toEqual('00000011');
// 通知処理が想定通りの引数で呼ばれているか確認
expect(notificationHubService.notify).toHaveBeenCalledWith(
makeContext('trackingId', 'requestId'),
[`user_${typistUserId}`],
{
authorId: 'AUTHOR_ID',
filename: 'file',
priority: 'High',
uploadedAt: '2023-05-26T11:22:33.444',
},
);
// 作成したタスクを取得
const resultTask = await getTaskFromJobNumber(source, result.jobNumber);
// タスクのチェックアウト権限を取得
const resultCheckoutPermission = await getCheckoutPermissions(
source,
resultTask?.id ?? 0,
);
// タスクのテンプレートファイルIDを確認
expect(resultTask?.template_file_id).toEqual(templateFileId);
// タスクのチェックアウト権限が想定通りワークフローで設定されているのユーザーIDで作成されているか確認
expect(resultCheckoutPermission.length).toEqual(1);
expect(resultCheckoutPermission[0].user_id).toEqual(typistUserId);
});
it('タスク作成時に採番されるジョブナンバーが常に最新タスクのジョブナンバー + 1となる(途中のタスクが削除されている場合)', async () => {
if (!source) fail();
const { id: accountId } = await makeTestSimpleAccount(source);
const {
external_id: authorExternalId,
id: authorUserId,
author_id: authorAuthorId,
} = await makeTestUser(source, {
account_id: accountId,
external_id: 'author-user-external-id',
role: 'author',
author_id: 'AUTHOR_ID',
});
const { id: typistUserId } = await makeTestUser(source, {
account_id: accountId,
external_id: 'typist-user-external-id',
role: 'typist',
author_id: undefined,
});
// ワークタイプを作成
const { id: worktypeId, custom_worktype_id } = await createWorktype(
source,
accountId,
'worktypeId',
);
// テンプレートファイルを作成
const { id: templateFileId } = await createTemplateFile(
source,
accountId,
'templateFile',
'http://blob/url/templateFile.zip',
);
// ワークフローを作成
const { id: workflowId } = await createWorkflow(
source,
accountId,
authorUserId,
worktypeId,
templateFileId,
);
// ワークフロータイピストを作成
await createWorkflowTypist(source, workflowId, typistUserId);
const blobParam = makeBlobstorageServiceMockValue();
const notificationParam = makeDefaultNotificationhubServiceMockValue();
// タスクを10件作成, ジョブナンバーは00000001から00000010まで
for (let i = 1; i <= 10; i++) {
await createTask(
source,
accountId,
`http://blob/url/file_${i}.zip`,
`file_${i}.zip`,
'Uploaded',
typistUserId,
authorAuthorId ?? '',
undefined,
undefined,
i.toString().padStart(8, '0'),
);
}
// 最新のジョブナンバーでjob_numberテーブルを作成
await createJobNumber(source, accountId, '00000010');
{
// 初期データ確認
const tasks = await getTasks(source, accountId);
expect(tasks.length).toEqual(10);
const jobNumber = await getJobNumber(source, accountId);
expect(jobNumber?.job_number).toEqual('00000010');
}
const module = await makeTestingModuleWithBlobAndNotification(
source,
blobParam,
notificationParam,
);
if (!module) fail();
const service = module.get<FilesService>(FilesService);
const taskService = module.get<TasksService>(TasksService);
const notificationHubService = module.get<NotificationhubService>(
NotificationhubService,
);
// 途中のジョブナンバーのタスクを取得
const latestTask = await getTaskFromJobNumber(source, '00000005');
await taskService.deleteTask(
makeContext('trackingId', 'requestId'),
authorExternalId,
latestTask?.audio_file_id ?? 0, // 最新タスクのaudioFileId
);
{
// タスク削除確認
const tasks = await getTasks(source, accountId);
expect(tasks.length).toEqual(9);
// JobNumberが00000005のタスクが存在しないことを確認
expect(
tasks.find((task) => task.job_number === '00000005'),
).toBeUndefined();
}
const result = await service.uploadFinished(
makeContext('trackingId', 'requestId'),
authorExternalId,
'http://blob/url/file.zip',
authorAuthorId ?? '',
'file.zip',
'11:22:33',
'2023-05-26T11:22:33.444',
'2023-05-26T11:22:33.444',
'2023-05-26T11:22:33.444',
256,
'01',
'DS2',
'comment',
custom_worktype_id,
optionItemList,
false,
);
expect(result.jobNumber).toEqual('00000011');
// job_numberテーブルが正しく更新されているか確認
const jobNumber = await getJobNumber(source, accountId);
expect(jobNumber?.job_number).toEqual('00000011');
// 通知処理が想定通りの引数で呼ばれているか確認
expect(notificationHubService.notify).toHaveBeenCalledWith(
makeContext('trackingId', 'requestId'),
[`user_${typistUserId}`],
{
authorId: 'AUTHOR_ID',
filename: 'file',
priority: 'High',
uploadedAt: '2023-05-26T11:22:33.444',
},
);
// 作成したタスクを取得
const resultTask = await getTaskFromJobNumber(source, result.jobNumber);
// タスクのチェックアウト権限を取得
const resultCheckoutPermission = await getCheckoutPermissions(
source,
resultTask?.id ?? 0,
);
// タスクのテンプレートファイルIDを確認
expect(resultTask?.template_file_id).toEqual(templateFileId);
// タスクのチェックアウト権限が想定通りワークフローで設定されているのユーザーIDで作成されているか確認
expect(resultCheckoutPermission.length).toEqual(1);
expect(resultCheckoutPermission[0].user_id).toEqual(typistUserId);
});
it('タスク作成時に採番されるジョブナンバーが常に最新タスクのジョブナンバー + 1となる(最新タスクのジョブナンバーが99999999で削除されている場合)', async () => {
if (!source) fail();
const { id: accountId } = await makeTestSimpleAccount(source);
const {
external_id: authorExternalId,
id: authorUserId,
author_id: authorAuthorId,
} = await makeTestUser(source, {
account_id: accountId,
external_id: 'author-user-external-id',
role: 'author',
author_id: 'AUTHOR_ID',
});
const { id: typistUserId } = await makeTestUser(source, {
account_id: accountId,
external_id: 'typist-user-external-id',
role: 'typist',
author_id: undefined,
});
// ワークタイプを作成
const { id: worktypeId, custom_worktype_id } = await createWorktype(
source,
accountId,
'worktypeId',
);
// テンプレートファイルを作成
const { id: templateFileId } = await createTemplateFile(
source,
accountId,
'templateFile',
'http://blob/url/templateFile.zip',
);
// ワークフローを作成
const { id: workflowId } = await createWorkflow(
source,
accountId,
authorUserId,
worktypeId,
templateFileId,
);
// ワークフロータイピストを作成
await createWorkflowTypist(source, workflowId, typistUserId);
const blobParam = makeBlobstorageServiceMockValue();
const notificationParam = makeDefaultNotificationhubServiceMockValue();
// タスクを10件作成, ジョブナンバーは99999989から99999999まで
for (let i = 99999989; i <= 99999999; i++) {
await createTask(
source,
accountId,
`http://blob/url/file_${i}.zip`,
`file_${i}.zip`,
'Uploaded',
typistUserId,
authorAuthorId ?? '',
undefined,
undefined,
i.toString().padStart(8, '0'),
);
}
// 最新のジョブナンバーでjob_numberテーブルを作成
await createJobNumber(source, accountId, '99999999');
{
// 初期データ確認
const tasks = await getTasks(source, accountId);
expect(tasks.length).toEqual(11);
const jobNumber = await getJobNumber(source, accountId);
expect(jobNumber?.job_number).toEqual('99999999');
}
const module = await makeTestingModuleWithBlobAndNotification(
source,
blobParam,
notificationParam,
);
if (!module) fail();
const service = module.get<FilesService>(FilesService);
const taskService = module.get<TasksService>(TasksService);
const notificationHubService = module.get<NotificationhubService>(
NotificationhubService,
);
// 最新のジョブナンバーのタスクを取得
const latestTask = await getTaskFromJobNumber(source, '99999999');
await taskService.deleteTask(
makeContext('trackingId', 'requestId'),
authorExternalId,
latestTask?.audio_file_id ?? 0, // 最新タスクのaudioFileId
);
{
// タスク削除確認
const tasks = await getTasks(source, accountId);
expect(tasks.length).toEqual(10);
// JobNumberが99999999のタスクが存在しないことを確認
expect(
tasks.find((task) => task.job_number === '99999999'),
).toBeUndefined();
}
const result = await service.uploadFinished(
makeContext('trackingId', 'requestId'),
authorExternalId,
'http://blob/url/file.zip',
authorAuthorId ?? '',
'file.zip',
'11:22:33',
'2023-05-26T11:22:33.444',
'2023-05-26T11:22:33.444',
'2023-05-26T11:22:33.444',
256,
'01',
'DS2',
'comment',
custom_worktype_id,
optionItemList,
false,
);
expect(result.jobNumber).toEqual('00000001');
// job_numberテーブルが正しく更新されているか確認
// 最新タスクのジョブナンバーが99999999の時、新規作成されたタスクのジョブナンバーは00000001になる
const jobNumber = await getJobNumber(source, accountId);
expect(jobNumber?.job_number).toEqual('00000001');
// 通知処理が想定通りの引数で呼ばれているか確認
expect(notificationHubService.notify).toHaveBeenCalledWith(
makeContext('trackingId', 'requestId'),
[`user_${typistUserId}`],
{
authorId: 'AUTHOR_ID',
filename: 'file',
priority: 'High',
uploadedAt: '2023-05-26T11:22:33.444',
},
);
// 作成したタスクを取得
const resultTask = await getTaskFromJobNumber(source, result.jobNumber);
// タスクのチェックアウト権限を取得
const resultCheckoutPermission = await getCheckoutPermissions(
source,
resultTask?.id ?? 0,
);
// タスクのテンプレートファイルIDを確認
expect(resultTask?.template_file_id).toEqual(templateFileId);
// タスクのチェックアウト権限が想定通りワークフローで設定されているのユーザーIDで作成されているか確認
expect(resultCheckoutPermission.length).toEqual(1);
expect(resultCheckoutPermission[0].user_id).toEqual(typistUserId);
});
});
describe('音声ファイルダウンロードURL取得', () => {

View File

@ -44,6 +44,7 @@ import {
import { UserGroup } from '../../../repositories/user_groups/entity/user_group.entity';
import { UserGroupMember } from '../../../repositories/user_groups/entity/user_group_member.entity';
import { License } from '../../../repositories/licenses/entity/license.entity';
import { JobNumber } from '../../../repositories/job_number/entity/job_number.entity';
export const createTask = async (
datasource: DataSource,
@ -109,6 +110,18 @@ export const createTask = async (
return { audioFileId: audioFile.id, taskId: task.id };
};
// job_numberテーブルにレコードを作成する
export const createJobNumber = async (
datasource: DataSource,
accountId: number,
jobNumber: string,
): Promise<void> => {
await datasource.getRepository(JobNumber).insert({
account_id: accountId,
job_number: jobNumber,
});
};
export const getTaskFromJobNumber = async (
datasource: DataSource,
jobNumber: string,

View File

@ -263,15 +263,6 @@ export const getTask = async (
return task;
};
export const getTasks = async (
datasource: DataSource,
account_id: number,
): Promise<Task[]> => {
const tasks = await datasource.getRepository(Task).find({
where: { account_id: account_id },
});
return tasks;
};
export const getCheckoutPermissions = async (
datasource: DataSource,

View File

@ -6,7 +6,7 @@ import {
getTemplateFiles,
updateTaskTemplateFile,
} from './test/utility';
import { makeTestAccount, makeTestUser } from '../../common/test/utility';
import { getTasks, makeTestAccount, makeTestUser } from '../../common/test/utility';
import { makeContext } from '../../common/log';
import { TemplateFilesRepositoryService } from '../../repositories/template_files/template_files.repository.service';
import { HttpException, HttpStatus } from '@nestjs/common';
@ -14,7 +14,7 @@ import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import { truncateAllTable } from '../../common/test/init';
import { overrideBlobstorageService } from '../../common/test/overrides';
import { TASK_STATUS, USER_ROLES } from '../../constants';
import { createTask, getTasks } from '../tasks/test/utility';
import { createTask } from '../tasks/test/utility';
import {
createWorkflow,
createWorkflowTypist,

View File

@ -0,0 +1,25 @@
import {
Entity,
Column,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
import { bigintTransformer } from '../../../common/entity';
@Entity({ name: 'job_number' })
export class JobNumber {
@PrimaryGeneratedColumn()
id: number;
@Column({ type: 'bigint', transformer: bigintTransformer })
account_id: number;
@Column()
job_number: string;
@UpdateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
updated_at: Date;
}

View File

@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { JobNumber } from './entity/job_number.entity';
import { JobNumberRepositoryService } from './job_number.repository.service';
@Module({
imports: [TypeOrmModule.forFeature([JobNumber])],
providers: [JobNumberRepositoryService],
exports: [JobNumberRepositoryService],
})
export class JobNumberRepositoryModule {}

View File

@ -0,0 +1,7 @@
import { Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm';
@Injectable()
export class JobNumberRepositoryService {
constructor(private dataSource: DataSource) {}
}

View File

@ -54,6 +54,7 @@ import {
} from '../../common/repository';
import { Context } from '../../common/log';
import { UserNotFoundError } from '../users/errors/types';
import { JobNumber } from '../job_number/entity/job_number.entity';
@Injectable()
export class TasksRepositoryService {
@ -905,25 +906,29 @@ export class TasksRepositoryService {
const taskRepo = entityManager.getRepository(Task);
// バグ 3954: [4/8リリース]タスクをすべてBackupした後、タスクを作成するとジョブナンバーがから採番される暫定対応
// アカウント内で最新タスクのタスクを取得し、そのJOBナンバーをインクリメントして新しいタスクのJOBナンバーを設定する
const lastTask = await taskRepo.findOne({
// バグ 3954: [4/8リリース]タスクをすべてBackupした後、タスクを作成するとジョブナンバーがから採番される恒久対応
// job_numberテーブルから最新のJOBナンバーを取得
const jobNumberRepo = entityManager.getRepository(JobNumber);
const currentJobNumberData = await jobNumberRepo.findOne({
where: { account_id: account_id },
order: { created_at: 'DESC', job_number: 'DESC' },
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
lock: { mode: 'pessimistic_write' },
});
// JOBナンバーが存在しない場合はエラー
// 運用上はあり得ないが、プログラム上発生しうるのでエラーとして処理
if (!currentJobNumberData) {
throw new Error(`JobNumber not exists. account_id:${account_id}`);
}
let newJobNumber = '00000001';
if (!lastTask) {
// 初回は00000001
newJobNumber = '00000001';
} else if (lastTask.job_number === '99999999') {
let newJobNumber: string = '';
if (currentJobNumberData.job_number === '99999999') {
// 末尾なら00000001に戻る
newJobNumber = '00000001';
} else {
// 最新のJOBナンバーをインクリメントして次の番号とする
newJobNumber = `${Number(lastTask.job_number) + 1}`.padStart(8, '0');
newJobNumber = `${
Number(currentJobNumberData.job_number) + 1
}`.padStart(8, '0');
}
task.job_number = newJobNumber;
@ -935,6 +940,15 @@ export class TasksRepositoryService {
context,
);
// JobNumberを更新
await updateEntity(
jobNumberRepo,
{ account_id: account_id },
{ job_number: newJobNumber },
this.isCommentOut,
context,
);
const optionItems = paramOptionItems.map((x) => {
return {
audio_file_id: persisted.audio_file_id,