Merged PR 723: [FB対応]タイピストグループ重複時のエラーとする

## 概要
[Task3613: 対応](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3613)

- タイピストグループ名が重複した際のエラーを追加
  - タイピストグループ追加API
  - タイピストグループ更新API
- タイピストグループ設定画面に表示するエラーメッセージを追加

## レビューポイント
- 行ロックするべきかどうか
  - ギリギリのタイミングで同名のタイピストグループが作成される場合は防げないのでDBでユニーク制約を設定する?
    - insertにロックはかけられないから

## UIの変更
- https://ndstokyo.sharepoint.com/:f:/r/sites/Piranha/Shared%20Documents/General/OMDS/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88/Task3613?csf=1&web=1&e=i8cN2g

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

## 補足
- 相談、参考資料などがあれば
This commit is contained in:
saito.k 2024-02-05 11:46:29 +00:00
parent 48d2e625db
commit 84b0da1f95
12 changed files with 105 additions and 18 deletions

View File

@ -54,6 +54,8 @@ export const errorCodes = [
"E010809", // ライセンス発行キャンセル不可エラー(ステータスが変えられている場合)
"E010810", // ライセンス発行キャンセル不可エラー(発行から一定期間経過した場合)
"E010811", // ライセンス発行キャンセル不可エラー(発行したライセンスが割り当てされている場合)
"E010908", // タイピストグループ不在エラー
"E010909", // タイピストグループ名重複エラー
"E011001", // ワークタイプ重複エラー
"E011002", // ワークタイプ登録上限超過エラー
"E011003", // ワークタイプ不在エラー

View File

@ -122,11 +122,17 @@ export const createTypistGroupAsync = createAsyncThunk<
} catch (e) {
// e ⇒ errorObjectに変換"
const error = createErrorObject(e);
const message =
error.statusCode === 400
? getTranslationID("typistGroupSetting.message.groupSaveFailedError")
: getTranslationID("common.message.internalServerError");
let message = getTranslationID("common.message.internalServerError");
if (error.code === "E010204") {
message = getTranslationID(
"typistGroupSetting.message.groupSaveFailedError"
);
}
if (error.code === "E010909") {
message = getTranslationID(
"typistGroupSetting.message.GroupNameAlreadyExistError"
);
}
thunkApi.dispatch(
openSnackbar({
@ -242,10 +248,17 @@ export const updateTypistGroupAsync = createAsyncThunk<
// e ⇒ errorObjectに変換"
const error = createErrorObject(e);
const message =
error.statusCode === 400
? getTranslationID("typistGroupSetting.message.groupSaveFailedError")
: getTranslationID("common.message.internalServerError");
let message = getTranslationID("common.message.internalServerError");
if (error.code === "E010204" || error.code === "E010908") {
message = getTranslationID(
"typistGroupSetting.message.groupSaveFailedError"
);
}
if (error.code === "E010909") {
message = getTranslationID(
"typistGroupSetting.message.GroupNameAlreadyExistError"
);
}
thunkApi.dispatch(
openSnackbar({

View File

@ -424,7 +424,10 @@
},
"message": {
"selectedTypistEmptyError": "Um eine Transkriptionsgruppe zu speichern, müssen ein oder mehrere Transkriptionisten ausgewählt werden.",
"groupSaveFailedError": "Die Transkriptionistengruppe konnte nicht gespeichert werden. Die angezeigten Informationen sind möglicherweise veraltet. Aktualisieren Sie daher bitte den Bildschirm, um den neuesten Status anzuzeigen."
"groupSaveFailedError": "Die Transkriptionistengruppe konnte nicht gespeichert werden. Die angezeigten Informationen sind möglicherweise veraltet. Aktualisieren Sie daher bitte den Bildschirm, um den neuesten Status anzuzeigen.",
"GroupNameAlreadyExistError": "(de)このTranscriptionistGroup名は既に登録されています。他のTranscriptionistGroup名で登録してください。",
"deleteFailedWorkflowAssigned": "(de)TranscriptionistGroupの削除に失敗しました。Workflow画面でルーティングルールから対象TranscriptionistGroupを外してください。",
"deleteFailedCheckoutPermissionExisted": "(de)TranscriptionistGroupの削除に失敗しました。Dictation画面でタスクのルーティングから対象TranscriptionistGroupを外してください。"
}
},
"worktypeIdSetting": {

View File

@ -424,7 +424,10 @@
},
"message": {
"selectedTypistEmptyError": "One or more transcriptonist must be selected to save a transcrption group.",
"groupSaveFailedError": "Transcriptionist Group could not be saved. The displayed information may be outdated, so please refresh the screen to see the latest status."
"groupSaveFailedError": "Transcriptionist Group could not be saved. The displayed information may be outdated, so please refresh the screen to see the latest status.",
"GroupNameAlreadyExistError": "このTranscriptionistGroup名は既に登録されています。他のTranscriptionistGroup名で登録してください。",
"deleteFailedWorkflowAssigned": "TranscriptionistGroupの削除に失敗しました。Workflow画面でルーティングルールから対象TranscriptionistGroupを外してください。",
"deleteFailedCheckoutPermissionExisted": "TranscriptionistGroupの削除に失敗しました。Dictation画面でタスクのルーティングから対象TranscriptionistGroupを外してください。"
}
},
"worktypeIdSetting": {
@ -568,4 +571,4 @@
"close": "Close"
}
}
}
}

View File

@ -424,7 +424,10 @@
},
"message": {
"selectedTypistEmptyError": "Se deben seleccionar uno o más transcriptores para guardar un grupo de transcripción.",
"groupSaveFailedError": "El grupo transcriptor no se pudo salvar. La información mostrada puede estar desactualizada. Así que actualice la pantalla para ver el estado más reciente."
"groupSaveFailedError": "El grupo transcriptor no se pudo salvar. La información mostrada puede estar desactualizada. Así que actualice la pantalla para ver el estado más reciente.",
"GroupNameAlreadyExistError": "(es)このTranscriptionistGroup名は既に登録されています。他のTranscriptionistGroup名で登録してください。",
"deleteFailedWorkflowAssigned": "(es)TranscriptionistGroupの削除に失敗しました。Workflow画面でルーティングルールから対象TranscriptionistGroupを外してください。",
"deleteFailedCheckoutPermissionExisted": "(es)TranscriptionistGroupの削除に失敗しました。Dictation画面でタスクのルーティングから対象TranscriptionistGroupを外してください。"
}
},
"worktypeIdSetting": {
@ -568,4 +571,4 @@
"close": "Cerrar"
}
}
}
}

View File

@ -424,7 +424,10 @@
},
"message": {
"selectedTypistEmptyError": "Un ou plusieurs transcripteurs doivent être sélectionnés pour enregistrer un groupe de transcription.",
"groupSaveFailedError": "Le groupe de transcriptionniste n'a pas pu être enregistré. Les informations affichées peuvent être obsolètes, veuillez donc actualiser l'écran pour voir le dernier statut."
"groupSaveFailedError": "Le groupe de transcriptionniste n'a pas pu être enregistré. Les informations affichées peuvent être obsolètes, veuillez donc actualiser l'écran pour voir le dernier statut.",
"GroupNameAlreadyExistError": "(fr)このTranscriptionistGroup名は既に登録されています。他のTranscriptionistGroup名で登録してください。",
"deleteFailedWorkflowAssigned": "(fr)TranscriptionistGroupの削除に失敗しました。Workflow画面でルーティングルールから対象TranscriptionistGroupを外してください。",
"deleteFailedCheckoutPermissionExisted": "(fr)TranscriptionistGroupの削除に失敗しました。Dictation画面でタスクのルーティングから対象TranscriptionistGroupを外してください。"
}
},
"worktypeIdSetting": {
@ -568,4 +571,4 @@
"close": "Fermer"
}
}
}
}

View File

@ -0,0 +1,6 @@
-- +migrate Up
ALTER TABLE `user_group` ADD UNIQUE `unique_index_account_id_name` (`account_id`, `name`);
-- +migrate Down
ALTER TABLE `user_group` DROP INDEX `unique_index_account_id_name`;

View File

@ -59,6 +59,7 @@ export const ErrorCodes = [
'E010811', // ライセンス発行キャンセル不可エラー(発行したライセンスが割り当てされている場合)
'E010812', // ライセンス未割当エラー
'E010908', // タイピストグループ不在エラー
'E010909', // タイピストグループ名重複エラー
'E011001', // ワークタイプ重複エラー
'E011002', // ワークタイプ登録上限超過エラー
'E011003', // ワークタイプ不在エラー

View File

@ -48,6 +48,7 @@ export const errors: Errors = {
E010811: 'Already license allocated Error',
E010812: 'License not allocated Error',
E010908: 'Typist Group not exist Error',
E010909: 'Typist Group name already exist Error',
E011001: 'This WorkTypeID already used Error',
E011002: 'WorkTypeID create limit exceeded Error',
E011003: 'WorkTypeID not found Error',

View File

@ -60,6 +60,7 @@ import {
} from '../../repositories/licenses/errors/types';
import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service';
import {
TypistGroupNameAlreadyExistError,
TypistGroupNotExistError,
TypistIdInvalidError,
} from '../../repositories/user_groups/errors/types';
@ -1241,6 +1242,12 @@ export class AccountsService {
makeErrorResponse('E010204'),
HttpStatus.BAD_REQUEST,
);
// 同名のタイピストグループが存在する場合は400エラーを返す
case TypistGroupNameAlreadyExistError:
throw new HttpException(
makeErrorResponse('E010909'),
HttpStatus.BAD_REQUEST,
);
default:
throw new HttpException(
makeErrorResponse('E009999'),
@ -1315,6 +1322,12 @@ export class AccountsService {
makeErrorResponse('E010908'),
HttpStatus.BAD_REQUEST,
);
// 同名のタイピストグループが存在する場合は400エラーを返す
case TypistGroupNameAlreadyExistError:
throw new HttpException(
makeErrorResponse('E010909'),
HttpStatus.BAD_REQUEST,
);
default:
throw new HttpException(
makeErrorResponse('E009999'),

View File

@ -12,3 +12,10 @@ export class TypistIdInvalidError extends Error {
this.name = 'TypistIdInvalidError';
}
}
// 同名のタイピストグループが存在する場合のエラー
export class TypistGroupNameAlreadyExistError extends Error {
constructor(message: string) {
super(message);
this.name = 'TypistGroupNameAlreadyExistError';
}
}

View File

@ -1,9 +1,13 @@
import { Injectable } from '@nestjs/common';
import { DataSource, In, IsNull } from 'typeorm';
import { DataSource, In, IsNull, Not } from 'typeorm';
import { UserGroup } from './entity/user_group.entity';
import { UserGroupMember } from './entity/user_group_member.entity';
import { User } from '../users/entity/user.entity';
import { TypistGroupNotExistError, TypistIdInvalidError } from './errors/types';
import {
TypistGroupNameAlreadyExistError,
TypistGroupNotExistError,
TypistIdInvalidError,
} from './errors/types';
import { USER_ROLES } from '../../constants';
import {
insertEntities,
@ -132,6 +136,19 @@ export class UserGroupsRepositoryService {
)}`,
);
}
// 同名のタイピストグループが存在するか確認する
const sameNameTypistGroup = await userGroupRepo.findOne({
where: {
name,
account_id: accountId,
},
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
});
if (sameNameTypistGroup) {
throw new TypistGroupNameAlreadyExistError(
`TypistGroup already exists Error. accountId: ${accountId}; name: ${name}`,
);
}
// userGroupをDBに保存する
const userGroup = await insertEntity(
UserGroup,
@ -200,6 +217,21 @@ export class UserGroupsRepositoryService {
);
}
// 同名のタイピストグループが存在するか確認する
const sameNameTypistGroup = await userGroupRepo.findOne({
where: {
id: Not(typistGroupId),
name: typistGroupName,
account_id: accountId,
},
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
});
if (sameNameTypistGroup) {
throw new TypistGroupNameAlreadyExistError(
`TypistGroup already exists Error. accountId: ${accountId}; name: ${typistGroupName}`,
);
}
// GroupIdが自アカウント内に存在するか確認する
const typistGroup = await userGroupRepo.findOne({
where: {