diff --git a/dictation_server/src/common/test/utility.ts b/dictation_server/src/common/test/utility.ts index 28c8b0c..6046a4f 100644 --- a/dictation_server/src/common/test/utility.ts +++ b/dictation_server/src/common/test/utility.ts @@ -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 => { + 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 => { + const jobNumber = await datasource.getRepository(JobNumber).findOne({ + where: { + account_id: account_id, + }, + }); + return jobNumber; +}; diff --git a/dictation_server/src/features/accounts/accounts.service.spec.ts b/dictation_server/src/features/accounts/accounts.service.spec.ts index 60ca795..99ed015 100644 --- a/dictation_server/src/features/accounts/accounts.service.spec.ts +++ b/dictation_server/src/features/accounts/accounts.service.spec.ts @@ -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'; diff --git a/dictation_server/src/features/files/files.service.spec.ts b/dictation_server/src/features/files/files.service.spec.ts index 0539eed..01d2b1e 100644 --- a/dictation_server/src/features/files/files.service.spec.ts +++ b/dictation_server/src/features/files/files.service.spec.ts @@ -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, ); + 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); 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); // メール送信関数が呼ばれたかどうかで判定を行う。実際のメール送信は行わない。 const spy = jest @@ -1004,6 +1033,9 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { ); } + // 最新のジョブナンバーでjob_numberテーブルを作成 + await createJobNumber(source, accountId, '00000004'); + const service = module.get(FilesService); // メール送信関数が呼ばれたかどうかで判定を行う。実際のメール送信は行わない。 const spy = jest @@ -1095,6 +1127,9 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { ); } + // 最新のジョブナンバーでjob_numberテーブルを作成 + await createJobNumber(source, accountId, '00000004'); + const service = module.get(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); + const taskService = module.get(TasksService); + const notificationHubService = module.get( + 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); + const taskService = module.get(TasksService); + const notificationHubService = module.get( + 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); + const taskService = module.get(TasksService); + const notificationHubService = module.get( + 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取得', () => { diff --git a/dictation_server/src/features/files/test/utility.ts b/dictation_server/src/features/files/test/utility.ts index c001e6a..9239a1f 100644 --- a/dictation_server/src/features/files/test/utility.ts +++ b/dictation_server/src/features/files/test/utility.ts @@ -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 => { + await datasource.getRepository(JobNumber).insert({ + account_id: accountId, + job_number: jobNumber, + }); +}; + export const getTaskFromJobNumber = async ( datasource: DataSource, jobNumber: string, diff --git a/dictation_server/src/features/tasks/test/utility.ts b/dictation_server/src/features/tasks/test/utility.ts index 31928e0..676f1ad 100644 --- a/dictation_server/src/features/tasks/test/utility.ts +++ b/dictation_server/src/features/tasks/test/utility.ts @@ -263,15 +263,6 @@ export const getTask = async ( return task; }; -export const getTasks = async ( - datasource: DataSource, - account_id: number, -): Promise => { - const tasks = await datasource.getRepository(Task).find({ - where: { account_id: account_id }, - }); - return tasks; -}; export const getCheckoutPermissions = async ( datasource: DataSource, diff --git a/dictation_server/src/features/templates/templates.service.spec.ts b/dictation_server/src/features/templates/templates.service.spec.ts index 37c3813..442dc87 100644 --- a/dictation_server/src/features/templates/templates.service.spec.ts +++ b/dictation_server/src/features/templates/templates.service.spec.ts @@ -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, diff --git a/dictation_server/src/repositories/job_number/entity/job_number.entity.ts b/dictation_server/src/repositories/job_number/entity/job_number.entity.ts new file mode 100644 index 0000000..ec2373e --- /dev/null +++ b/dictation_server/src/repositories/job_number/entity/job_number.entity.ts @@ -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; +} diff --git a/dictation_server/src/repositories/job_number/job_number.repository.module.ts b/dictation_server/src/repositories/job_number/job_number.repository.module.ts new file mode 100644 index 0000000..a39f9bd --- /dev/null +++ b/dictation_server/src/repositories/job_number/job_number.repository.module.ts @@ -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 {} diff --git a/dictation_server/src/repositories/job_number/job_number.repository.service.ts b/dictation_server/src/repositories/job_number/job_number.repository.service.ts new file mode 100644 index 0000000..5790f00 --- /dev/null +++ b/dictation_server/src/repositories/job_number/job_number.repository.service.ts @@ -0,0 +1,7 @@ +import { Injectable } from '@nestjs/common'; +import { DataSource } from 'typeorm'; + +@Injectable() +export class JobNumberRepositoryService { + constructor(private dataSource: DataSource) {} +} diff --git a/dictation_server/src/repositories/tasks/tasks.repository.service.ts b/dictation_server/src/repositories/tasks/tasks.repository.service.ts index d5b2a37..1e30896 100644 --- a/dictation_server/src/repositories/tasks/tasks.repository.service.ts +++ b/dictation_server/src/repositories/tasks/tasks.repository.service.ts @@ -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した後、タスクを作成するとジョブナンバーが1から採番される(暫定対応) - // アカウント内で最新タスクのタスクを取得し、そのJOBナンバーをインクリメントして新しいタスクのJOBナンバーを設定する - const lastTask = await taskRepo.findOne({ + // バグ 3954: [4/8リリース]タスクをすべてBackupした後、タスクを作成するとジョブナンバーが1から採番される(恒久対応) + // 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,