diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index d952519f4..dc646a0f2 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -24,7 +24,7 @@ Issue や Discussions に対して追加の説明や質問がある場合は、 これらでは解決できない場合、 Discussions の [Q&A](https://github.com/AlesInfiny/maia/discussions/categories/05-q-a) より質問をお寄せください。 質問を投稿する場合、直面していることについてできる限り多くの情報を提供してください。 -可能な限りスピーディーに対応いたします。 +可能な限り迅速に対応いたします。 ただし、質問に対する回答が常に得られることを期待しないでください。 ## 本プロジェクトに対するコントリビュート diff --git a/.textlintrc b/.textlintrc index 070fe80de..fa98dd79b 100644 --- a/.textlintrc +++ b/.textlintrc @@ -358,6 +358,7 @@ "状態", "ジョッキー", "ジレンマ", + "迅速", "スキャナー", "スキーマ", "スクエア", @@ -377,7 +378,6 @@ "ストーブ", "ストーリー", "スパイウェア", - "スピーディー", "スピード", "スペイン", "スポーツ", diff --git a/cspell.json b/cspell.json index 03fa44d09..234003a71 100644 --- a/cspell.json +++ b/cspell.json @@ -30,6 +30,8 @@ "words": [ "Adoptium", "AlesInfiny", + "Appender", + "Appenders", "BIPROGY", "Checkstyle", "Corretto", @@ -44,6 +46,7 @@ "JaCoCo", "JDBC", "linenums", + "Logback", "markdownlint", "mkdocs", "Mockito", diff --git a/documents/_materials/images/app-architecture/client-side-rendering/error-message-delivery-dark.drawio b/documents/_materials/images/app-architecture/client-side-rendering/error-message-delivery-dark.drawio new file mode 100644 index 000000000..6a1e29c50 --- /dev/null +++ b/documents/_materials/images/app-architecture/client-side-rendering/error-message-delivery-dark.drawio @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/documents/_materials/images/app-architecture/client-side-rendering/error-message-delivery-light.drawio b/documents/_materials/images/app-architecture/client-side-rendering/error-message-delivery-light.drawio new file mode 100644 index 000000000..3a77c9e75 --- /dev/null +++ b/documents/_materials/images/app-architecture/client-side-rendering/error-message-delivery-light.drawio @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/documents/contents/app-architecture/client-side-rendering/frontend-application/index.md b/documents/contents/app-architecture/client-side-rendering/frontend-application/index.md index caa59229f..fb7e1b3e9 100644 --- a/documents/contents/app-architecture/client-side-rendering/frontend-application/index.md +++ b/documents/contents/app-architecture/client-side-rendering/frontend-application/index.md @@ -155,13 +155,17 @@ Vue.js プロジェクトのフォルダー構成は、ブランクプロジェ ├─ cypress/ ------------------ cypress による E2E テストに関するファイルを格納します。 ├─ public/ ------------------- メディアファイルや favicon など静的な資産を格納します。 ├─ src/ +│ ├─ api-client/ ------------ API クライアントの設定ファイルを格納します。 │ ├─ assets/ ---------------- コードや動的ファイルが必要とするCSSや画像などのアセットを格納します。 │ ├─ components/ ------------ 単体で自己完結している再利用性の高い vue コンポーネントなどを格納します。 │ ├─ config/ ---------------- 設定ファイルを格納します。 │ ├─ generated/ ------------- 自動生成されたファイルを格納します。 +│ ├─ locales/ --------------- メッセージ管理に関するファイルを格納します。 │ ├─ router/ ---------------- ルーティング定義を格納します。 │ ├─ services/ -------------- サービスに関するファイルを格納します。 +│ ├─ shared/ ---------------- アプリケーション全体で再利用する共通機能のファイルを格納します。 │ ├─ stores/ ---------------- store に関するファイルを格納します。 +│ ├─ validation/ ------------ 一元化するバリデーション定義ファイルを格納します。 │ ├─ views/ ----------------- ルーティングで指定される vue ファイルを格納します。またページ固有の挙動などもここに含めます。 │ ├─ App.vue │ └─ main.ts diff --git a/documents/contents/app-architecture/client-side-rendering/global-function/exception-handling.md b/documents/contents/app-architecture/client-side-rendering/global-function/exception-handling.md index 14ada9c8b..7099e8f4b 100644 --- a/documents/contents/app-architecture/client-side-rendering/global-function/exception-handling.md +++ b/documents/contents/app-architecture/client-side-rendering/global-function/exception-handling.md @@ -3,6 +3,8 @@ title: CSR 編 description: クライアントサイドレンダリングを行う Web アプリケーションの アーキテクチャについて解説します。 --- + + # 例外処理方針 {#top} バックエンドアプリケーションで発生するシステム例外や業務例外は、例外フィルターによって捕捉します。 @@ -103,3 +105,61 @@ HTTP 通信で発生する例外について、レスポンスやステータス C->>B: エラー通知 deactivate C ``` + +### API 通信のエラーレスポンス {#error-response} + +API 通信で発生した例外において、バックエンドから返却されるエラーレスポンスには、 [ProblemDetails :material-open-in-new:](https://www.rfc-editor.org/rfc/rfc9457.html){ target=_blank } に基づくレスポンスボディーを含みます。 + +ProblemDetails は、 HTTP API のエラーレスポンスを標準化するための構造体であり、以下のプロパティで定義されています。 + +| 項目 | プロパティ名 | 推奨レベル | 内容 | +| ---------------------------- | ------------ | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 簡易メッセージ | title | 必須 | 人間が読める形式で提供されます。 | +| 詳細メッセージ | detail | 本番環境は非推奨 | スタックトレースなどを含めます。開発環境では生産性向上を目的に含めることを推奨しますが、本番環境では使用しません。開発環境と本番環境で構成を変更することが難しい場合には detail を使用しないよう統一することも検討してください。 | +| ステータスコード | status | 任意 | HTTP エラーのステータスコードです。 | +| 詳細 URL | type | 推奨 | エラーを説明する詳細 Web ドキュメントがある場合、ユーザーが参照する URL を追加します。 | +| 問題が発生したリソースの URI | instance | 任意 | 問題の発生場所を示す URI です。リクエスト先と異なるリソースが問題の発生したリソースである場合、実装の詳細やデータなどの内部情報が漏洩する可能性があるため、追加には注意が必要です。 | +| 任意のパラメータ | | 任意 | 拡張メンバーです。必要に応じて ProblemDetails のプロパティを拡張する場合に利用します。 | + +AlesInfiny Maia OSS Edition では、上記のプロパティに加えて拡張メンバーとして以下を定義しています。 + +- exceptionId + + 例外 ID 。メッセージファイルからエラーメッセージを取得するためのメッセージコードとして利用します。 + +- exceptionValues + + 例外値。例外 ID から取得したエラーメッセージ内のパラメーターに代入する値として利用します。 + +クライアントサイドでポップアップやトースト通知などを用いてエラー通知を実装する場合には、 ProblemDetails の各プロパティの内容を表示します。 + +#### エラーレスポンスの例 {#example-of-error-response} + +説明したエラーレスポンスの例を以下に示します。 + +```json title="開発環境の場合のエラーレスポンス" +HTTP/1.1 400 Bad Request +Content-Type: application/json; charset=utf-8 + +{ + "type": "https://hoge.com/error/catalogItemIdDoesNotExistInBasket", + "title": "業務エラーが発生しました。", + "status": 400, + "detail": "dressca.applicationcore.baskets.CatalogItemInBasketNotFoundException: 業務エラーが発生しました。 ###以下スタックトレースは省略###", + "exceptionId": "catalogItemIdDoesNotExistInBasket", + "exceptionValues": ["1 ###買い物かごID###", "10 ###商品ID###"] +} +``` + +```json title="本番環境の場合のエラーレスポンス" +HTTP/1.1 400 Bad Request +Content-Type: application/json; charset=utf-8 + +{ + "type": "https://hoge.com/error/catalogItemIdDoesNotExistInBasket", + "title": "業務エラーが発生しました。", + "status": 400, + "exceptionId": "catalogItemIdDoesNotExistInBasket", + "exceptionValues": ["1 ###買い物かごID###", "10 ###商品ID###"] +} +``` diff --git a/documents/contents/app-architecture/client-side-rendering/global-function/message-management-policy.md b/documents/contents/app-architecture/client-side-rendering/global-function/message-management-policy.md index 4ee181928..89675769a 100644 --- a/documents/contents/app-architecture/client-side-rendering/global-function/message-management-policy.md +++ b/documents/contents/app-architecture/client-side-rendering/global-function/message-management-policy.md @@ -9,5 +9,136 @@ Java アプリケーションのメッセージ管理方針については、以 - [Java アプリケーションの処理方式 - メッセージ管理方針](../../../app-architecture/overview/java-application-processing-system/message-management-policy.md#message-management-policy) -フロントエンドアプリケーションでは、特別なメッセージ管理しません。 -画面内に出力するメッセージやラベルは、各画面やソースコード内に個別に実装したものを使用します。 +フロントエンドアプリケーションにおけるメッセージとは、画面や帳票等に表示する固定文言、またはユーザーの画面操作の結果に応じて表示する動的文言を指します。 + +## メッセージパターン {#message-pattern} + +フロントエンドアプリケーションにおけるメッセージパターンとメッセージの表示内容を以下に示します。 + +| パターン | 表示内容 | 表示例 | +| -------------------- | -------------------- | ------------------------------------------------------------ | +| 処理メッセージ | 処理の確認 | 注文内容を確認して「注文を確定する」ボタンを押してください。 | +| | 正常終了 | 注文が完了しました。 | +| | 業務エラー | カートに追加できませんでした。 | +| | | 商品の削除に失敗しました。 | +| | システムエラー | サーバーエラーが発生しました。 | +| | | ネットワークエラーが発生しました。 | +| 入力値検証メッセージ | 単項目チェックエラー | 値を入力してください | +| | 相関チェックエラー | パスワードと確認用パスワードが一致しません | +| ラベル | 画面のタイトル | Dressca | +| | 画面の項目名 | 合計 / 送料 | +| | UI ラベル | 注文を確定する | + +## メッセージの管理単位 {#management-unit} + +各メッセージの用途を明確にするため、前述したメッセージパターンごとにメッセージを管理します。 +AlesInfiny Maia OSS Edition における、メッセージパターンごとのメッセージ管理方針は以下の通りです。 + +- 処理メッセージ + + 処理メッセージ管理用のファイルを定義して一括で管理します。 + + 処理メッセージは変更や追加の要求が多く、一か所で管理することによりメンテナンスが容易になります。 + +- 入力値検証メッセージ + + 入力値検証メッセージ管理用のファイルを定義して一括で管理します。 + + 入力値検証メッセージは複数の場所で使用する可能性が高く、ファイルに定義しておくことで再利用が簡単になります。 + これにより、コードの重複を避けることにつながります。 + +- ラベル + + 画面内に出力するラベルは各画面やソースコード内に個別に実装したものを使用します。 + + ラベルに対してもアプリケーション全体で用語を統一する場合や後述する多言語を実施する場合には、ファイルによるメッセージの一元管理が必要です。 + しかし、用語の統一は初期の実装やメッセージ管理ファイルの維持にかかるコストが高いです。 + また、ラベルへの多言語対応は言語設定によって画面のレイアウトが崩れてしまう恐れがあります。 + そのため、ラベルに対してファイルによるメッセージ管理を導入することは、慎重な検討が必要です。 + +## メッセージのファイル管理 {#message-file-management} + +フロントエンドアプリケーションでは、処理メッセージや入力値検証メッセージは JSON ファイルを利用して管理します。 +JSON ファイルでは、以下のようにメッセージ文字列を識別するメッセージコードとメッセージ文字列本体を key-value で管理します。 + +``` json title="メッセージの JSON ファイルの定義例" +{ + "errorOccurred": "エラーが発生しました。", + ... +} +``` + +### メッセージコードの定義 {#message-codes-definition} + +前述した JSON ファイルのように、メッセージコードはそのメッセージの内容を簡潔に表す文字列とします。 +このようにすることで、以下のような利点があります。 + +- 可読性の向上 + + 数字のメッセージコードよりも、意味を持つメッセージコードを定義することで直感的で理解しやすい利点があります。 + 開発者がメッセージコードを見たときに、すぐにそのメッセージの内容や目的を把握できます。 + +- エラー追跡の効率化 + + ログやデバッグ時に、意味を持つコードは問題を迅速に特定することに役立ちます。 + +- ドキュメント化の簡便さ + + メッセージコードがそのメッセージ内容を簡潔に表していれば、メッセージコードの意味を説明するための追加のドキュメントが不要になることがあります。 + +### エラーメッセージコードの統一 {#unification-of-message-codes} + +エラー発生時のメッセージ整形の流れを以下に示します。 + +![エラーメッセージ整形の流れ](../../../images/app-architecture/client-side-rendering/error-message-delivery-light.png#only-light){ loading=lazy } +![エラーメッセージ整形の流れ](../../../images/app-architecture/client-side-rendering/error-message-delivery-dark.png#only-dark){ loading=lazy } + +この画像の通り、エラー発生時はバックエンドアプリケーションのエラーログの出力とフロントエンドアプリケーションへのエラーの画面出力が順次実施されます。 + +そのため、同一の業務エラーやシステムエラーのメッセージコードは、バックエンド側とフロントエンド側で統一します。 +これにより、以下のような利点があります。 + +- 一貫性の確保 + + 統一されたメッセージコードを使用することで、エラーの識別が容易になり、システム全体の一貫性が保たれます。 + +- デバッグの効率化 + + 開発者がエラーを特定しやすくなり、問題解決のスピードが向上します。 + バックエンドとフロントエンドで同じコードを使用することで、エラーの原因を迅速に特定できます。 + +OpenAPI 仕様書などの各 JSON ファイルと命名規則を統一するため、メッセージコードをキャメルケースとします。 +それに伴い、フロントエンド側に合わせてバックエンドのプロパティファイルのメッセージコードもキャメルケースで記載します。 +バックエンドのプロパティファイルの設定例は、[こちら](../global-function/message-management-policy.md#message-codes-definition) を確認してください。 + +### エラーメッセージ内のパラメータ {#parameter-of-error-messages} + +以下のように、エラーメッセージの中にはパラメータを含むものがあります。 + +```json title="パラメータを含むエラーメッセージの例" +{ + "assetNotFound": "アセットコード: [0] のアセットが見つかりませんでした。", +} +``` + +パラメータに代入するべき値は、エラーレスポンスに含まれる ProblemDetails の拡張メンバーである `exceptionValues` から取得します。 +ProblemDetails については、[こちら](./exception-handling.md#error-response) を確認してください。 + +### 多言語対応 {#localization} + +メッセージを多言語対応する場合には、それぞれの言語の JSON ファイルを作成し、各言語のメッセージをフォルダーで分割して管理します。 +以下に示すように、フォルダー名は [ISO-639 言語コード :material-open-in-new:](https://www.iso.org/iso-639-language-code){ target=_blank } に基づき、その言語を表す言語コードとします。 + +また、各ファイルの末尾には言語コードを付与します。 + +```terminal linenums="0" +locales/ ------------------------------------- メッセージ管理を行うコードが配置されるフォルダー + ├ en/ ---------------------------------------- 英語メッセージの管理を行うフォルダー + │ ├ messageList_en.json ---------------------- 処理の成功や失敗などの結果メッセージを格納する JSON ファイル(英語) + │ └ validationTextList_en.json --------------- 入力値検証用のメッセージを格納する JSON ファイル(英語) + └ ja/ ---------------------------------------- 日本語メッセージの管理を行うフォルダー + ├ messageList_ja.json ---------------------- 処理の成功や失敗などの結果メッセージを格納する JSON ファイル(日本語) + └ validationTextList_ja.json --------------- 入力値検証用のメッセージを格納する JSON ファイル(日本語) +``` + +メッセージ管理方針に従った機能の実装方法などの詳細については、[こちら](../../../guidebooks/how-to-develop/vue-js/message-management.md) を確認してください。 diff --git a/documents/contents/app-architecture/overview/java-application-processing-system/logging-policy.md b/documents/contents/app-architecture/overview/java-application-processing-system/logging-policy.md index 9bc0cad1b..8bc940ba6 100644 --- a/documents/contents/app-architecture/overview/java-application-processing-system/logging-policy.md +++ b/documents/contents/app-architecture/overview/java-application-processing-system/logging-policy.md @@ -93,3 +93,32 @@ AlesInfiny Maia で定義するログの種類は以下の通りです。 AlesInfiny Maia では、 Java アプリケーションのロギングライブラリとして [Apache Log4j 2 :material-open-in-new:](https://logging.apache.org/log4j/2.x/){ target=_blank } を使用します。 またロギングファサードとして [SLF4J :material-open-in-new:](https://www.slf4j.org/){ target=_blank } を使用します。 + +### ログの設定ファイル {#logging-configuration-files} + +log4j 2 では、設定ファイルを使用しログの動作を制御します。 + +ログの設定ファイルは、以下の形式をサポートしています。 +なお、 AlesInfiny Maia では、ログの設定ファイルに XML 形式を採用しています。 + +- XML +- JSON +- YAML +- Properties + +また、 AlesInfiny Maia はマルチプロジェクト構成を採用しています。 +エントリーポイントとなるサブプロジェクトが複数ある場合、それぞれでログ形式を定めることが考えられます。 + +そのため、以下のようにログの設定ファイルをエントリーポイントとなる各プロジェクトに配置します。 + +```terminal linenums="0" +root/ ------------------------------------------ root フォルダー + ├ webapp1/ ---------------------------------- 管理アプリのエントリーポイントとなるサブプロジェクト + │ └ src/main/resource/ ------------------------ 設定ファイルを一元管理するフォルダー + │ └ log4j2.xml ------------------------------ ログの設定ファイル + └ webapp2/ ------------------------------- コンシューマーアプリのエントリーポイントとなるサブプロジェクト 2 + └ src/main/resource/ ------------------------ 設定ファイルを一元管理するフォルダー + └ log4j2.xml ------------------------------ ログの設定ファイル +``` + +log4j2.xml の具体的な設定については、[こちら](../../../guidebooks/how-to-develop/java/sub-project-settings/web-project-settings.md#logging-configuration) を確認してください。 diff --git a/documents/contents/app-architecture/overview/java-application-processing-system/message-management-policy.md b/documents/contents/app-architecture/overview/java-application-processing-system/message-management-policy.md index 0571dba32..fab227489 100644 --- a/documents/contents/app-architecture/overview/java-application-processing-system/message-management-policy.md +++ b/documents/contents/app-architecture/overview/java-application-processing-system/message-management-policy.md @@ -3,6 +3,74 @@ title: Java アプリケーションの 処理方式 description: アプリケーションの形態によらず、 Java アプリケーションで 考慮すべき関心事について、実装方針を説明します。 --- -## メッセージ管理方針 {#message-management-policy} + -メッセージ文字列は、表記の統一を図る目的にプロパティファイルで管理します。 +# メッセージ管理方針 {#message-management-policy} + +フロントエンドアプリケーションのメッセージ管理方針については、以下を参照してください。 + +- [フロントエンドアプリケーションの処理方式 - メッセージ管理方針](../../client-side-rendering/global-function/message-management-policy.md) + +メッセージ文字列は、表記の統一を図ることを目的にプロパティファイルで管理します。 + +## プロパティファイルの管理 {#property-file-management} + +プロパティファイルでは、以下のようにメッセージ文字列を識別するメッセージコードとメッセージ文字列本体をペアで管理します。 + +```properties title="message.properties の例" +errorOccurred=エラーが発生しました。 +... +``` + +## エラーメッセージコードの統一 {#unification-of-message-codes} + +同一の業務エラーやシステムエラーのメッセージコードは、バックエンド側とフロントエンド側で統一します。 + +詳細については、[こちら](../../client-side-rendering/global-function/message-management-policy.md#unification-of-message-codes) を確認してください。 + +## メッセージの管理単位 {#management-unit} + +以下のように、ビジネスロジックで発生する業務メッセージと共通処理として発生する共通メッセージを分割して各サブプロジェクトで管理します。 + +```terminal linenums="0" +root/ ------------------------------------------ root フォルダー + ├ application-core/src/main/resource + │ └ applicationcore --------------------------- 業務メッセージのプロパティファイルを一括管理するフォルダー + │ └ message.properties --------------------- 業務メッセージのプロパティファイル + └ system-common/src/main/resource + └ systemcommon ------------------------------ 共通メッセージのプロパティファイルを一括管理するフォルダー + └ message.properties --------------------- 共通メッセージのプロパティファイル +``` + +業務メッセージと共通メッセージとして格納するメッセージの例は以下の通りです。 + +- 業務メッセージの例 + + - 各業務例外発生時にログ出力するためのエラーメッセージ + - 業務完了時に正常終了した旨をログに出力するためのメッセージ + +- 共通メッセージの例 + + - システム例外発生時にログ出力するためのエラーメッセージ + - 業務例外やシステム例外の既定メッセージ + +## 多言語対応 {#localization} + +メッセージを多言語対応する場合には、それぞれの言語のプロパティファイルを作成し、各言語のメッセージをファイルで分割して管理します。 +以下に示すように、各ファイル名は [ISO-639 言語コード :material-open-in-new:](https://www.iso.org/iso-639-language-code){ target=_blank } に基づき、その言語を表す言語コードを末尾に付与します。 + +```terminal linenums="0" +root/ ------------------------------------------ root フォルダー + ├ application-core/src/main/resource + │ └ applicationcore --------------------------- 業務メッセージのプロパティファイルを一括管理するフォルダー + │ ├ message_en.properties ------------------ 業務メッセージのプロパティファイル(英語) + │ └ message_ja.properties ------------------ 業務メッセージのプロパティファイル(日本語) + └ system-common/src/main/resource + └ systemcommon ------------------------------ 共通メッセージのプロパティファイルを一括管理するフォルダー + ├ message_en.properties ------------------ 共通メッセージのプロパティファイル(英語) + └ message_ja.properties ------------------ 共通メッセージのプロパティファイル(日本語) +``` + +アプリケーション起動時に使用するメッセージファイルを切り替えることで、開発者に応じた言語を設定します。 + +メッセージ管理方針に従った機能の実装方法などの詳細については、[こちら](../../../guidebooks/how-to-develop/java/common-project-settings.md) を確認してください。 diff --git a/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/application-core-project-settings.md b/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/application-core-project-settings.md index f84cdefbf..59af86e69 100644 --- a/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/application-core-project-settings.md +++ b/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/application-core-project-settings.md @@ -22,6 +22,25 @@ dependencies { } ``` +## ロギングライブラリの除外設定 {#logging-library-exclusion-settings} + + + +依存関係に記載している `org.springframework.boot:spring-boot-starter` ライブラリは、デフォルトで Logback 用のライブラリである `org.springframework.boot:spring-boot-starter-logging` が推移的依存で追加されます。 + + + +AlesInfiny Maia OSS Edition では、ロギングライブラリとして log4j 2 を使用します。 +そのため、以下のようにデフォルトのロギングライブラリを依存関係から除外する設定を記述します。 + +``` groovy title="spring-boot-starter-logging の除外設定" +configurations { + all { + exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' + } +} +``` + ## プロジェクトのビルド方法の設定 {#config-build} application-core プロジェクトは単体で動作せず、他プロジェクトからライブラリとして呼び出します。 @@ -74,6 +93,13 @@ application-core プロジェクトの `src` 以下にある、 `ApplicationCore // その他、プロジェクトに必要な依存ライブラリは任意で追加してください。 } + configurations { + all { + exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' + } + } + + tasks.named('test') { useJUnitPlatform() } diff --git a/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/batch-project-settings.md b/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/batch-project-settings.md index 20423973d..f10b8ab7f 100644 --- a/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/batch-project-settings.md +++ b/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/batch-project-settings.md @@ -16,6 +16,8 @@ batch プロジェクトで利用を推奨するライブラリは以下の通 - `spring-boot-starter-batch`: Spring Batch アプリケーションを構築するための依存関係を提供するスターター +- `spring-boot-starter-log4j2`: Spring Boot アプリケーションで log4j 2 を使用するためのスターター + - `spring-batch-test`: Spring Batch アプリケーションのテストのライブラリ - `spring-boot-starter-test`:Spring Boot アプリケーションをテストするためのスターター @@ -23,6 +25,7 @@ batch プロジェクトで利用を推奨するライブラリは以下の通 ```groovy title="batch/build.gradle" dependencies { implementation 'org.springframework.boot:spring-boot-starter-batch' + implementation 'org.springframework.boot:spring-boot-starter-log4j2' testImplementation 'org.springframework.batch:spring-batch-test:x.x.x' testImplementation 'org.springframework.boot:spring-boot-starter-test' } @@ -101,6 +104,25 @@ batch プロジェクトの `src/main/resource` 以下に `application.propertie そのため、バッチアプリケーションの起動時に [メタデータテーブルを作成するスキーマ :material-open-in-new:](https://spring.pleiades.io/spring-batch/reference/schema-appendix.html){ target=_blank } を実行するよう指定する必要があります。 バッチ処理のジョブ管理をクラウドサービスや特定のジョブ管理ツールに任せる場合など、Spring Batch で生成されるメタデータテーブルを利用したくない際の対処法は [こちら](../../../../app-architecture/batch-application/batch-application-consideration/without-using-meta-data-table.md) をご覧ください。 +## ロギングライブラリの除外設定 {#logging-library-exclusion-settings} + + + +依存関係に記載している `org.springframework.boot:spring-boot-starter-batch` ライブラリは、デフォルトで Logback 用のライブラリである `org.springframework.boot:spring-boot-starter-logging` が推移的依存で追加されます。 + + + +AlesInfiny Maia OSS Edition では、ロギングライブラリとして log4j 2 を使用します。 +そのため、以下のようにデフォルトのロギングライブラリを依存関係から除外する設定を記述します。 + +``` groovy title="spring-boot-starter-logging の除外設定" +configurations { + all { + exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' + } +} +``` + ## バッチアプリケーションとして動作させる設定 {#config-batch-application} batch プロジェクトをウェブアプリケーションではなく、バッチアプリケーションとして動作させるためクラスファイルを書き換えます。 @@ -161,6 +183,7 @@ class BatchApplicationTests { dependencies { implementation 'org.springframework.boot:spring-boot-starter-batch' + implementation 'org.springframework.boot:spring-boot-starter-log4j2' testImplementation 'org.springframework.batch:spring-batch-test:x.x.x' testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation project(':application-core') @@ -169,6 +192,12 @@ class BatchApplicationTests { // その他、プロジェクトに必要な依存ライブラリは任意で追加してください。 } + configurations { + all { + exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' + } + } + tasks.named('test') { useJUnitPlatform() } diff --git a/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/infrastructure-project-settings.md b/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/infrastructure-project-settings.md index 5016a7348..3fa387f8c 100644 --- a/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/infrastructure-project-settings.md +++ b/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/infrastructure-project-settings.md @@ -43,6 +43,25 @@ dependencies { } ``` +## ロギングライブラリの除外設定 {#logging-library-exclusion-settings-infra} + + + +依存関係に記載している `org.mybatis.spring.boot:mybatis-spring-boot-starter` ライブラリは、デフォルトで Logback 用のライブラリである `org.springframework.boot:spring-boot-starter-logging` が推移的依存で追加されます。 + + + +AlesInfiny Maia OSS Edition では、ロギングライブラリとして log4j 2 を使用します。 +そのため、以下のようにデフォルトのロギングライブラリを依存関係から除外する設定を記述します。 + +``` groovy title="spring-boot-starter-logging の除外設定" +configurations { + all { + exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' + } +} +``` + ## プロジェクトのビルド方法の設定 {#config-build} infrastructure プロジェクトは単体で動作せず、他プロジェクトからライブラリとして呼び出します。 @@ -98,6 +117,13 @@ infrastructure プロジェクトの `src` 以下にある、 `InfrastructureApp // その他、プロジェクトに必要な依存ライブラリは任意で追加してください。 } + configurations { + all { + exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' + } + } + + bootJar { enabled = false } diff --git a/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/project-version-control.md b/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/project-version-control.md index 971d5763e..8e0ec1614 100644 --- a/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/project-version-control.md +++ b/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/project-version-control.md @@ -112,7 +112,7 @@ buildscript { Groovy では文字列は一重引用符で囲み、変数を含む文字列は二重引用符で囲んで表現します。 変数を含む文字列を一重引用符で囲むとエラーが出るため、注意して使い分けてください。 -```groovy title="web/build.gradle" hl_lines="3 4 5 9 10 11 12 13" +```groovy title="web/build.gradle" hl_lines="3 4 5 9 10 11 12 13 14" plugins { id 'java' id 'org.springframework.boot' version "${springBootVersion}" @@ -123,8 +123,9 @@ plugins { dependencies { implementation supportDependencies.spring_boot_starter_web implementation supportDependencies.h2database - implementation supportDependencies.springdoc_openapi_starter_webmvc_ui - implementation supportDependencies.spring_boot_starter_actuator + implementation supportDependencies.springdoc_openapi_starter_webmvc_ui + implementation supportDependencies.spring_boot_starter_actuator + implementation supportDependencies.spring_boot_starter_log4j2 testImplementation supportDependencies.spring_boot_starter_test implementation project(':application-core') implementation project(':infrastructure') @@ -158,8 +159,9 @@ dependencies { dependencies { implementation supportDependencies.spring_boot_starter_web implementation supportDependencies.h2database - implementation supportDependencies.springdoc_openapi_starter_webmvc_ui - implementation supportDependencies.spring_boot_starter_actuator + implementation supportDependencies.springdoc_openapi_starter_webmvc_ui + implementation supportDependencies.spring_boot_starter_actuator + implementation supportDependencies.spring_boot_starter_log4j2 testImplementation supportDependencies.spring_boot_starter_test implementation project(':application-core') implementation project(':infrastructure') @@ -167,6 +169,12 @@ dependencies { // その他、プロジェクトに必要な依存ライブラリは任意で追加してください。 } + configurations { + all { + exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' + } + } + // OpenAPI 仕様書出力の作業ディレクトリを指定する。 afterEvaluate { tasks.named("forkedSpringBootRun") { diff --git a/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/system-common-project-settings.md b/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/system-common-project-settings.md index 1def70570..cf544cbdd 100644 --- a/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/system-common-project-settings.md +++ b/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/system-common-project-settings.md @@ -10,6 +10,25 @@ system-common プロジェクトの依存ライブラリについては、特に また、 system-common プロジェクトは他のプロジェクトを参照する想定はないので、他のプロジェクトを依存関係に含める必要はありません。 +## ロギングライブラリの除外設定 {#logging-library-exclusion-settings} + + + +依存関係に記載している `org.springframework.boot:spring-boot-starter` ライブラリは、デフォルトで Logback 用のライブラリである `org.springframework.boot:spring-boot-starter-logging` が推移的依存で追加されます。 + + + +AlesInfiny Maia OSS Edition では、ロギングライブラリとして log4j 2 を使用します。 +そのため、以下のようにデフォルトのロギングライブラリを依存関係から除外する設定を記述します。 + +``` groovy title="spring-boot-starter-logging の除外設定" +configurations { + all { + exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' + } +} +``` + ## プロジェクトのビルド方法の設定 {#config-build} system-common プロジェクトは単体で動作せず、他プロジェクトからライブラリとして呼び出します。 @@ -61,6 +80,13 @@ system-common プロジェクトの `src` 以下にある、 `SystemCommonApplic // その他、プロジェクトに必要な依存ライブラリは任意で追加してください。 } + configurations { + all { + exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' + } + } + + bootJar { enabled = false } diff --git a/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/web-project-settings.md b/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/web-project-settings.md index ddcb44597..c942f1ae7 100644 --- a/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/web-project-settings.md +++ b/documents/contents/guidebooks/how-to-develop/java/sub-project-settings/web-project-settings.md @@ -20,6 +20,8 @@ web プロジェクトで利用を推奨するライブラリは以下の通り - `spring-boot-starter-actuator`: ヘルスチェックを含めたアプリケーション監視・管理機能を構築するためのスターター +- `spring-boot-starter-log4j2`: Spring Boot アプリケーションで log4j 2 を使用するためのスターター + - `spring-boot-starter-test`:Spring Boot アプリケーションをテストするためのスターター 上記のライブラリを依存ライブラリとして、 以下のように `build.gradle` の `dependencies` ブロックに追加します。 @@ -30,6 +32,7 @@ dependencies { implementation 'com.h2database:h2:x.x.x' implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:x.x.x' implementation 'org.springframework.boot:spring-boot-starter-actuator' + implementation 'org.springframework.boot:spring-boot-starter-log4j2' testImplementation 'org.springframework.boot:spring-boot-starter-test' } ``` @@ -101,6 +104,71 @@ web プロジェクトの `src/main/resource` 以下に `application.properties` spring.sql.init.mode=never ``` +## ロギングライブラリの除外設定 {#logging-library-exclusion-settings} + + + +依存関係に記載している `org.springframework.boot:spring-boot-starter` ライブラリは、デフォルトで Logback 用のライブラリである `org.springframework.boot:spring-boot-starter-logging` が推移的依存で追加されます。 + + + +AlesInfiny Maia OSS Edition では、ロギングライブラリとして log4j 2 を使用します。 +そのため、以下のようにデフォルトのロギングライブラリを依存関係から除外する設定を記述します。 + +``` groovy title="spring-boot-starter-logging の除外設定" +configurations { + all { + exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' + } +} +``` + +## ログの設定 {#logging-configuration} + +`src/main/resource` に `log4j2.xml` ファイルを配置しログの設定を記述します。 +以下は、ログの設定例です。 + +```xml title="log4j2.xml" + + + + + + + + + + + + + + + + + + + + + + + + +``` + +log4j2.xml のタグの構成要素は以下の通りです。 + +- Appenders + + ログの出力先を指定します。 + ログイベントをどのリソース(コンソール、ファイル、データベースなど)に送信するかを決定します。 + +- Loggers + + ログのエントリーポイントを指定します。 + この設定では、どのレベルのメッセージをログに記録するかや、 Appenders のどの要素にメッセージを送信するかなどを指定します。 + +その他の詳細な設定については、[公式ページ :material-open-in-new:](https://logging.apache.org/log4j/2.x/manual/configuration.html){ target=_blank } を確認してください。 + ## OpenAPI 仕様書の出力設定 {#open-api-specification-output-configuration} OpenAPI 仕様書のファイルがビルド時に出力されるようプロジェクトファイルを設定します。 @@ -170,6 +238,7 @@ build.dependsOn("generateOpenApiDocs") implementation 'com.h2database:h2:x.x.x' implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:x.x.x' implementation 'org.springframework.boot:spring-boot-starter-actuator' + implementation 'org.springframework.boot:spring-boot-starter-log4j2' testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation project(':application-core') implementation project(':infrastructure') @@ -177,6 +246,13 @@ build.dependsOn("generateOpenApiDocs") // その他、プロジェクトに必要な依存ライブラリは任意で追加してください。 } + configurations { + all { + exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' + } + } + + // OpenAPI 仕様書出力の作業ディレクトリを指定する。 afterEvaluate { tasks.named("forkedSpringBootRun") { diff --git a/documents/contents/guidebooks/how-to-develop/vue-js/index.md b/documents/contents/guidebooks/how-to-develop/vue-js/index.md index 60d5ab26a..180c3b11c 100644 --- a/documents/contents/guidebooks/how-to-develop/vue-js/index.md +++ b/documents/contents/guidebooks/how-to-develop/vue-js/index.md @@ -16,6 +16,7 @@ Vue.js を用いてフロントエンドアプリケーションを開発する 1. [開発に使用するパッケージ](optional-packages.md) 1. [CSS の設定と CSS フレームワークの適用](css.md) 1. [静的コード分析とフォーマット](static-verification-and-format.md) +1. [メッセージ管理機能の設定](message-management.md) 1. [入力値検証](input-validation.md) 1. [OpenAPI 仕様書からのクライアントコード生成](create-api-client-code.md) 1. [エラーハンドラーの設定](error-handler-settings.md) diff --git a/documents/contents/guidebooks/how-to-develop/vue-js/input-validation.md b/documents/contents/guidebooks/how-to-develop/vue-js/input-validation.md index 72aba8824..31528b1a4 100644 --- a/documents/contents/guidebooks/how-to-develop/vue-js/input-validation.md +++ b/documents/contents/guidebooks/how-to-develop/vue-js/input-validation.md @@ -6,36 +6,66 @@ description: Vue.js を用いた クライアントサイドアプリケーシ # 入力値検証 {#top} フロントエンドのアーキテクチャに基づき、入力値検証には VeeValidate と yup を使用します。 +また、入力値検証失敗時のメッセージを管理するために、 Vue I18n を使用します。 +メッセージ管理機能の実装方法の詳細に関しては、[こちら](./message-management.md) を確認してください。 ## 必要なパッケージのインストール {#install-packages} -ターミナルを開き、以下のコマンドを実行します。 +ターミナルを開き、対象プロジェクトのワークスペースフォルダーで以下のコマンドを実行します。 ```terminal -npm install vee-validate yup +npm install vee-validate yup vue-i18n +``` + +## メッセージの定義 {#definition-messages} + +入力値検証失敗時のメッセージを定義するため、`./src/locales` フォルダーに JSON ファイルを作成し、以下のように記述します。 +メッセージを多言語対応する場合には、それぞれの言語の JSON ファイルを作成し、各言語のメッセージをフォルダーで分割して管理します。 + +```json title="validationTextList_jp.json" +{ + "email": "メールアドレスの形式で入力してください", + "required": "値を入力してください" +} ``` ## 入力値検証時の設定 {#settings-validation} -yup のデフォルトのメッセージは英語であるため、日本語のメッセージを設定します。[アーキテクチャ定義](../../../app-architecture/client-side-rendering/frontend-application/index.md#project-structure) では設定ファイルは `./src/config` フォルダーに集約されるため、ファイル `./src/config/yup.config.ts` を作成し、以下のように記述します。 +各言語設定に基づいた、入力値検証メッセージを読み込みます。 +[アーキテクチャ定義](../../../app-architecture/client-side-rendering/frontend-application/index.md#project-structure) では設定ファイルは `./src/config` フォルダーに集約されるため、ファイル `./src/config/yup.config.ts` を作成し、以下のように記述します。 ```typescript title="yup.config.ts" +import { i18n } from '@/locales/i18n'; import { setLocale } from 'yup'; -setLocale({ - mixed: { - required: '値を入力してください', - }, - string: { - email: 'メールアドレスの形式で入力してください', - }, -}); +export function configureYup(): void { + const { t } = i18n.global; + setLocale({ + mixed: { + required: t('required'), + }, + string: { + email: t('email'), + }, + }); +} ``` -作成したファイルを読み込むため、 main.ts に import を記述します。 +作成したファイルを読み込むため、 入力値を検証する Vue ファイルのスクリプト構文に以下を記述します。 + +```vue title="example.vue" + ``` ## 入力値検証の実行 {#input-validation} diff --git a/documents/contents/guidebooks/how-to-develop/vue-js/message-management.md b/documents/contents/guidebooks/how-to-develop/vue-js/message-management.md new file mode 100644 index 000000000..c2937e597 --- /dev/null +++ b/documents/contents/guidebooks/how-to-develop/vue-js/message-management.md @@ -0,0 +1,167 @@ +--- +title: Vue.js 開発手順 +description: Vue.js を用いた クライアントサイドアプリケーションの 開発手順を説明します。 +--- + +# メッセージ管理機能の設定 {#top} + +フロントエンドのメッセージ管理方針に関するアーキテクチャについては、[こちら](../../../app-architecture/client-side-rendering/global-function/message-management-policy.md) をご確認ください。 +本アーキテクチャに基づき、メッセージ管理機能のライブラリは [Vue I18n :material-open-in-new:](https://kazupon.github.io/vue-i18n/){ target=\_blank } を使用します。 + +## 必要なパッケージのインストール {#install-packages} + +ターミナルを開き、対象プロジェクトのワークスペースフォルダーで以下のコマンドを実行します。 + +```terminal +npm install vue-i18n +``` + +## 設定方法 {#settings} + +本設定で利用するフォルダーの構成は以下の通りです。 + +```terminal linenums="0" + + └ src/ ------------------------------------------- アプリケーションのソースコードが配置されるフォルダー + ├ locales/ ------------------------------------- メッセージ管理を行うコードが配置されるフォルダー + │ ├ en/ ---------------------------------------- 英語メッセージの管理を行うフォルダー + │ │ ├ messageList_en.json ---------------------- 処理の成功や失敗などの結果メッセージを格納する JSON ファイル(英語) + │ │ └ validationTextList_en.json --------------- 入力値検証用のメッセージを格納する JSON ファイル(英語) + │ ├ ja/ ---------------------------------------- 日本語メッセージの管理を行うフォルダー + │ │ ├ messageList_ja.json ---------------------- 処理の成功や失敗などの結果メッセージを格納する JSON ファイル(日本語) + │ │ └ validationTextList_ja.json --------------- 入力値検証用のメッセージを格納する JSON ファイル(日本語) + │ └ i18n.ts ------------------------------------ メッセージ管理の設定に関するコード + └ main.ts -------------------------------------- 各ライブラリ等を読み込むためのコード +``` + +### メッセージファイルの作成 {#creating-message-files} + +メッセージに関するファイルは `./src/locales` フォルダーに集約します。 +以下のように、メッセージ本体を格納する JSON ファイルを作成します。 + +```json title="messageList_jp.json の例" +{ + "errorOccurred": "エラーが発生しました。", + "businessError": "業務エラーが発生しました。", + "loginRequiredError": "ログインしてください。", + ... +} +``` + +前述のフォルダー構成の通り、メッセージ管理方針に従って JSON ファイルは以下の 2 つに分割します。 + +- messageList.json + + 処理の成功や失敗を示す処理メッセージを格納します。 + +- validationTextList.json + + 入力値検証用のメッセージを格納します。 + 詳細は [こちら](./input-validation.md#definition-messages) を確認してください。 + +JSON ファイルでメッセージを管理する際は、メッセージコードとメッセージ本体を key-value で管理します。 + +### メッセージファイルの読込 {#reading-message-files} + +メッセージ本体を格納する JSON ファイルを読み込むために、以下のように `i18n.ts` を実装します。 + +```ts title="i18n.ts" +import { createI18n } from "vue-i18n"; +import messageListEN from '@/locales/en/messageList_en.json'; +import messageListJA from '@/locales/ja/messageList_ja.json'; +import validationTextListJA from '@/locales/ja/validationTextList_ja.json'; +import validationTextListEN from '@/locales/en/validationTextList_en.json'; + +const langPackage = { + ja: { + ...messageListJA, + ...validationTextListJA, + }, + en: { + ...messageListEN, + ...validationTextListEN, + }, +}; + +const i18n = createI18n({ + legacy: false, + locale: window.navigator.language, + fallbackLocale: "en", + messages: langPackage, +}); + +export { i18n }; +``` + +メッセージ管理機能を導入するための `createI18n` の引数の役割は以下の通りです。 + +- legacy + + + + createI18n のインスタンスとして、 [Legacy API :material-open-in-new:](https://vue-i18n.intlify.dev/api/legacy.html){ target=\_blank } と [Composition API :material-open-in-new:](https://vue-i18n.intlify.dev/api/composition.html){ target=\_blank } のどちらを利用するか選択します。 + 本実装では、 Composition API を利用するため、 legacy を false に設定します。 + + +- locale + + 使用する言語を指定します。 + 本実装では、`window.navigator.language` でブラウザーの言語設定を取得します。 + +- fallbackLocale + + locale に設定した言語がサポートされていない場合に、フォールバックする locale を指定します。 + +- messages + + locale の言語設定に基づき、利用するメッセージを指定します。 + +`i18n.ts` の設定をアプリケーションに反映させるため、 `main.ts` に以下のように実装します。 + +```ts title="main.ts" hl_lines="6 14" +import { createApp } from "vue"; +import { createPinia } from "pinia"; +import { authenticationGuard } from "@/shared/authentication/authentication-guard"; +import App from "./App.vue"; +import { router } from "./router"; +import { i18n } from "./locales/i18n"; + +import "@/assets/base.css"; + +const app = createApp(App); + +app.use(createPinia()); +app.use(router); +app.use(i18n); + +authenticationGuard(router); + +app.mount("#app"); +``` + +### メッセージの取得 {#getting-messages} + +読み込んだメッセージを取得するためには、 `i18n.ts` を各ファイルでインポートして利用します。 +実装例は以下の通りです。 + +```ts title="メッセージ利用例" + + +// テンプレート構文上で利用する場合 + +``` + +メッセージ関数 `t()` を利用してメッセージを取得します。 +メッセージ関数 `t()` の引数には、 JSON ファイルのメッセージコードを指定してメッセージ本体を呼び出します。 diff --git a/documents/contents/guidebooks/how-to-develop/vue-js/optional-packages.md b/documents/contents/guidebooks/how-to-develop/vue-js/optional-packages.md index d02dc79c1..82e8ee85c 100644 --- a/documents/contents/guidebooks/how-to-develop/vue-js/optional-packages.md +++ b/documents/contents/guidebooks/how-to-develop/vue-js/optional-packages.md @@ -94,3 +94,7 @@ description: Vue.js を用いた クライアントサイドアプリケーシ - [yup :material-open-in-new:](https://github.com/jquense/yup){ target=_blank } JavaScript でフォームのバリデーションルールを宣言的に記述できるライブラリ([インストール方法](input-validation.md#install-packages)) + +- [Vue I18n :material-open-in-new:](https://vue-i18n.intlify.dev/){ target=_blank } + + Vue.js アプリケーションでメッセージを外部ファイルで一元管理したり、多言語対応するためのライブラリ([インストール方法](input-validation.md#install-packages)) diff --git a/documents/contents/images/app-architecture/client-side-rendering/error-message-delivery-dark.png b/documents/contents/images/app-architecture/client-side-rendering/error-message-delivery-dark.png new file mode 100644 index 000000000..111f7b4a5 Binary files /dev/null and b/documents/contents/images/app-architecture/client-side-rendering/error-message-delivery-dark.png differ diff --git a/documents/contents/images/app-architecture/client-side-rendering/error-message-delivery-light.png b/documents/contents/images/app-architecture/client-side-rendering/error-message-delivery-light.png new file mode 100644 index 000000000..bf882d98c Binary files /dev/null and b/documents/contents/images/app-architecture/client-side-rendering/error-message-delivery-light.png differ diff --git a/documents/contents/index.md b/documents/contents/index.md index 4e7e3facd..088fd06ee 100644 --- a/documents/contents/index.md +++ b/documents/contents/index.md @@ -96,15 +96,13 @@ VS Code のターミナルで、「dressca\\dressca-frontend\\consumer」に移 1. VS Code で「dressca\\dressca-backend」フォルダーを開き、必要な拡張機能をインストールします。 「拡張機能」メニューから 「拡張機能のフィルター」>「推奨」>「ワークスペース推奨事項」にある拡張機能を全てインストールします。 -1. VS Code のターミナルで、カレントフォルダーを「dressca\\dressca-backend」にして以下のコマンドを実行してビルドします。 +1. VS Code のアクティビティーバーにある「 Gradle 」をクリックし、サイドバーの「 GRADLE PROJECTS 」タブから以下のタスクを実行します。 - ```winbatch title="バックエンドアプリケーションのビルド" - ./gradlew build - ``` + dressca-backend > Tasks > build > build -1. VS Code のアクティビティーバーにある「Gradle」をクリックし、サイドバーの「GRADLE PROJECTS」タブから以下のタスクを実行します。 +1. 前手順と同様に、サイドバーの「 GRADLE PROJECTS 」タブから以下のタスクを実行します。 - web > Tasks > application > bootRun + dressca-backend > Tasks > application > bootRunDev 1. 以下のアドレスで、サンプルアプリケーションの API にアクセスできます。 diff --git a/documents/mkdocs.yml b/documents/mkdocs.yml index a050fbde4..af0430db4 100644 --- a/documents/mkdocs.yml +++ b/documents/mkdocs.yml @@ -100,6 +100,7 @@ nav: - 開発に使用するパッケージ: guidebooks/how-to-develop/vue-js/optional-packages.md - CSS の設定と CSS フレームワークの適用: guidebooks/how-to-develop/vue-js/css.md - 静的コード分析とフォーマット: guidebooks/how-to-develop/vue-js/static-verification-and-format.md + - メッセージ管理機能の設定: guidebooks/how-to-develop/vue-js/message-management.md - 入力値検証: guidebooks/how-to-develop/vue-js/input-validation.md - Open API 仕様書からのクライアントコード生成: guidebooks/how-to-develop/vue-js/create-api-client-code.md - エラーハンドラーの設定: guidebooks/how-to-develop/vue-js/error-handler-settings.md diff --git a/samples/azure-ad-b2c-sample/auth-backend/system-common/build.gradle b/samples/azure-ad-b2c-sample/auth-backend/system-common/build.gradle index 4c470c72e..197b5f5df 100644 --- a/samples/azure-ad-b2c-sample/auth-backend/system-common/build.gradle +++ b/samples/azure-ad-b2c-sample/auth-backend/system-common/build.gradle @@ -19,7 +19,6 @@ repositories { dependencies { implementation supportDependencies.spring_boot_starter - implementation supportDependencies.spring_boot_starter_log4j2 testImplementation supportDependencies.spring_boot_starter_test } diff --git a/samples/azure-ad-b2c-sample/auth-backend/web/build.gradle b/samples/azure-ad-b2c-sample/auth-backend/web/build.gradle index 168bd43e3..42e9580f9 100644 --- a/samples/azure-ad-b2c-sample/auth-backend/web/build.gradle +++ b/samples/azure-ad-b2c-sample/auth-backend/web/build.gradle @@ -23,6 +23,7 @@ dependencies { implementation supportDependencies.springdoc_openapi_starter_webmvc_ui implementation supportDependencies.spring_cloud_azure_starter implementation supportDependencies.spring_cloud_azure_starter_ad_b2c + implementation supportDependencies.spring_boot_starter_log4j2 implementation project(':system-common') diff --git a/samples/azure-ad-b2c-sample/auth-backend/system-common/src/main/resources/log4j2.xml b/samples/azure-ad-b2c-sample/auth-backend/web/src/main/resources/log4j2.xml similarity index 100% rename from samples/azure-ad-b2c-sample/auth-backend/system-common/src/main/resources/log4j2.xml rename to samples/azure-ad-b2c-sample/auth-backend/web/src/main/resources/log4j2.xml diff --git a/samples/web-csr/dressca-backend/.vscode/launch.json b/samples/web-csr/dressca-backend/.vscode/launch.json index 1f81dd98a..b005e1e69 100644 --- a/samples/web-csr/dressca-backend/.vscode/launch.json +++ b/samples/web-csr/dressca-backend/.vscode/launch.json @@ -8,7 +8,7 @@ "console": "internalConsole", "mainClass": "com.dressca.web.WebApplication", "projectName": "web", - "args": "", + "args": "--spring.profiles.active=local", "envFile": "${workspaceFolder}/.env" }, { diff --git a/samples/web-csr/dressca-backend/application-core/build.gradle b/samples/web-csr/dressca-backend/application-core/build.gradle index a3e936786..9a8bd7b77 100644 --- a/samples/web-csr/dressca-backend/application-core/build.gradle +++ b/samples/web-csr/dressca-backend/application-core/build.gradle @@ -20,7 +20,6 @@ repositories { dependencies { implementation supportDependencies.spring_boot_starter implementation supportDependencies.mybatis_spring_boot_starter - implementation supportDependencies.spring_boot_starter_validation implementation supportDependencies.commons_lang3 implementation project(':system-common') diff --git a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/applicationservice/AssetApplicationService.java b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/applicationservice/AssetApplicationService.java index f910ad4a8..63497bcbe 100644 --- a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/applicationservice/AssetApplicationService.java +++ b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/applicationservice/AssetApplicationService.java @@ -13,7 +13,7 @@ import com.dressca.applicationcore.assets.AssetRepository; import com.dressca.applicationcore.assets.AssetResourceInfo; import com.dressca.applicationcore.assets.AssetStore; -import com.dressca.systemcommon.constant.MessageIdConstant; +import com.dressca.applicationcore.constant.MessageIdConstant; import com.dressca.systemcommon.constant.SystemPropertyConstants; import lombok.AllArgsConstructor; @@ -44,7 +44,9 @@ public class AssetApplicationService { public AssetResourceInfo getAssetResourceInfo(String assetCode) throws AssetNotFoundException { apLog - .debug(messages.getMessage(MessageIdConstant.D_ASSET0001_LOG, new Object[] { assetCode }, Locale.getDefault())); + .debug(messages.getMessage(MessageIdConstant.D_ASSET_GET_ASSET, + new Object[] { assetCode }, + Locale.getDefault())); Asset asset = this.repository.findByAssetCode(assetCode) .orElseThrow(() -> new AssetNotFoundException(assetCode)); diff --git a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/applicationservice/CatalogApplicationService.java b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/applicationservice/CatalogApplicationService.java index f0cd22be4..97ac71827 100644 --- a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/applicationservice/CatalogApplicationService.java +++ b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/applicationservice/CatalogApplicationService.java @@ -14,7 +14,7 @@ import com.dressca.applicationcore.catalog.CatalogCategoryRepository; import com.dressca.applicationcore.catalog.CatalogItem; import com.dressca.applicationcore.catalog.CatalogRepository; -import com.dressca.systemcommon.constant.MessageIdConstant; +import com.dressca.applicationcore.constant.MessageIdConstant; import com.dressca.systemcommon.constant.SystemPropertyConstants; import lombok.AllArgsConstructor; @@ -46,7 +46,7 @@ public class CatalogApplicationService { */ public List getCatalogItems(long brandId, long categoryId, int page, int pageSize) { - apLog.debug(messages.getMessage(MessageIdConstant.D_CATALOG0001_LOG, + apLog.debug(messages.getMessage(MessageIdConstant.D_CATALOG_GET_CATALOG_ITEMS, new Object[] { brandId, categoryId, page, pageSize }, Locale.getDefault())); return this.catalogRepository.findByBrandIdAndCategoryId(brandId, categoryId, page, pageSize); @@ -61,7 +61,8 @@ public List getCatalogItems(long brandId, long categoryId, int page */ public int countCatalogItems(long brandId, long categoryId) { - apLog.debug(messages.getMessage(MessageIdConstant.D_CATALOG0002_LOG, new Object[] { brandId, categoryId }, + apLog.debug(messages.getMessage(MessageIdConstant.D_CATALOG_COUNT_CATALOG_ITEMS, + new Object[] { brandId, categoryId }, Locale.getDefault())); return this.catalogRepository.countByBrandIdAndCategoryId(brandId, categoryId); @@ -74,7 +75,9 @@ public int countCatalogItems(long brandId, long categoryId) { */ public List getBrands() { - apLog.debug(messages.getMessage(MessageIdConstant.D_CATALOG0003_LOG, new Object[] {}, Locale.getDefault())); + apLog.debug(messages.getMessage(MessageIdConstant.D_CATALOG_GET_BRANDS, + new Object[] {}, + Locale.getDefault())); return this.brandRepository.getAll(); } @@ -86,7 +89,9 @@ public List getBrands() { */ public List getCategories() { - apLog.debug(messages.getMessage(MessageIdConstant.D_CATALOG0004_LOG, new Object[] {}, Locale.getDefault())); + apLog.debug(messages.getMessage(MessageIdConstant.D_CATALOG_GET_CATEGORIES, + new Object[] {}, + Locale.getDefault())); return this.catalogCategoryRepository.getAll(); } diff --git a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/applicationservice/OrderApplicationService.java b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/applicationservice/OrderApplicationService.java index cd089801c..7036439d0 100644 --- a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/applicationservice/OrderApplicationService.java +++ b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/applicationservice/OrderApplicationService.java @@ -1,9 +1,9 @@ package com.dressca.applicationcore.applicationservice; +import com.dressca.applicationcore.constant.MessageIdConstant; import com.dressca.applicationcore.order.Order; import com.dressca.applicationcore.order.OrderNotFoundException; import com.dressca.applicationcore.order.OrderRepository; -import com.dressca.systemcommon.constant.MessageIdConstant; import com.dressca.systemcommon.constant.SystemPropertyConstants; import lombok.AllArgsConstructor; import java.util.Locale; @@ -40,7 +40,9 @@ public class OrderApplicationService { public Order getOrder(long orderId, String buyerId) throws OrderNotFoundException { apLog.debug( - messages.getMessage(MessageIdConstant.D_ORDER0001_LOG, new Object[] { orderId, buyerId }, Locale.getDefault())); + messages.getMessage(MessageIdConstant.D_ORDER_GET_ORDER, + new Object[] { orderId, buyerId }, + Locale.getDefault())); Order order = this.orderRepository.findById(orderId) .orElseThrow(() -> new OrderNotFoundException(null, orderId, buyerId)); diff --git a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/applicationservice/ShoppingApplicationService.java b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/applicationservice/ShoppingApplicationService.java index 09241d2d1..466289e4c 100644 --- a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/applicationservice/ShoppingApplicationService.java +++ b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/applicationservice/ShoppingApplicationService.java @@ -19,6 +19,7 @@ import com.dressca.applicationcore.catalog.CatalogItem; import com.dressca.applicationcore.catalog.CatalogNotFoundException; import com.dressca.applicationcore.catalog.CatalogRepository; +import com.dressca.applicationcore.constant.MessageIdConstant; import com.dressca.applicationcore.order.CatalogItemOrdered; import com.dressca.applicationcore.order.EmptyBasketOnCheckoutException; import com.dressca.applicationcore.order.Order; @@ -26,8 +27,7 @@ import com.dressca.applicationcore.order.OrderItemAsset; import com.dressca.applicationcore.order.OrderRepository; import com.dressca.applicationcore.order.ShipTo; -import com.dressca.systemcommon.constant.ExceptionIdConstant; -import com.dressca.systemcommon.constant.MessageIdConstant; +import com.dressca.systemcommon.constant.CommonExceptionIdConstant; import com.dressca.systemcommon.constant.SystemPropertyConstants; import com.dressca.systemcommon.exception.SystemException; import lombok.AllArgsConstructor; @@ -61,7 +61,7 @@ public class ShoppingApplicationService { public void addItemToBasket(String buyerId, long catalogItemId, int quantity) throws CatalogNotFoundException { - apLog.debug(messages.getMessage(MessageIdConstant.D_BASKET0001_LOG, + apLog.debug(messages.getMessage(MessageIdConstant.D_SHOPPING_ADD_ITEM_TO_BASKET, new Object[] { buyerId, catalogItemId, quantity }, Locale.getDefault())); Basket basket = getOrCreateBasketForUser(buyerId); @@ -87,7 +87,8 @@ public void addItemToBasket(String buyerId, long catalogItemId, int quantity) public void setQuantities(String buyerId, Map quantities) throws CatalogNotFoundException, CatalogItemInBasketNotFoundException { - apLog.debug(messages.getMessage(MessageIdConstant.D_BASKET0002_LOG, new Object[] { buyerId, quantities }, + apLog.debug(messages.getMessage(MessageIdConstant.D_SHOPPING_SET_BASKET_ITEMS_QUANTITIES, + new Object[] { buyerId, quantities }, Locale.getDefault())); Basket basket = getOrCreateBasketForUser(buyerId); @@ -123,7 +124,8 @@ public void setQuantities(String buyerId, Map quantities) */ public BasketDetail getBasketDetail(String buyerId) { - apLog.debug(messages.getMessage(MessageIdConstant.D_BASKET0003_LOG, new Object[] { buyerId }, Locale.getDefault())); + apLog.debug(messages.getMessage(MessageIdConstant.D_SHOPPING_GET_BASKET_ITEMS, + new Object[] { buyerId }, Locale.getDefault())); Basket basket = getOrCreateBasketForUser(buyerId); List catalogItemIds = basket.getItems().stream() @@ -144,7 +146,8 @@ public BasketDetail getBasketDetail(String buyerId) { public Order checkout(String buyerId, ShipTo shipToAddress) throws EmptyBasketOnCheckoutException { - apLog.debug(messages.getMessage(MessageIdConstant.D_BASKET0004_LOG, new Object[] { buyerId, shipToAddress }, + apLog.debug(messages.getMessage(MessageIdConstant.D_SHOPPING_CHECKOUT, + new Object[] { buyerId, shipToAddress }, Locale.getDefault())); Basket basket = getOrCreateBasketForUser(buyerId); @@ -186,7 +189,7 @@ private Basket createBasket(String buyerId) { private OrderItem mapToOrderItem(BasketItem basketItem, List catalogItems) { CatalogItem catalogItem = catalogItems.stream() .filter(c -> c.getId() == basketItem.getCatalogItemId()).findFirst() - .orElseThrow(() -> new SystemException(null, ExceptionIdConstant.E_SHARE0000, null, null)); + .orElseThrow(() -> new SystemException(null, CommonExceptionIdConstant.E_BUSINESS, null, null)); CatalogItemOrdered itemOrdered = new CatalogItemOrdered(catalogItem.getId(), catalogItem.getName(), catalogItem.getProductCode()); OrderItem orderItem = new OrderItem(itemOrdered, basketItem.getUnitPrice(), basketItem.getQuantity()); diff --git a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/assets/AssetNotFoundException.java b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/assets/AssetNotFoundException.java index 58916709a..a406f8fb3 100644 --- a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/assets/AssetNotFoundException.java +++ b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/assets/AssetNotFoundException.java @@ -1,6 +1,6 @@ package com.dressca.applicationcore.assets; -import com.dressca.systemcommon.constant.ExceptionIdConstant; +import com.dressca.applicationcore.constant.ExceptionIdConstant; import com.dressca.systemcommon.exception.LogicException; /** @@ -14,7 +14,7 @@ public class AssetNotFoundException extends LogicException { * @param assetCode 見つからなかった買い物かご Id */ public AssetNotFoundException(String assetCode) { - super(null, ExceptionIdConstant.E_ASSET0001, new String[] { assetCode }, + super(null, ExceptionIdConstant.E_ASSET_NOT_FOUND, new String[] { assetCode }, new String[] { assetCode }); } } diff --git a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/baskets/BasketNotFoundException.java b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/baskets/BasketNotFoundException.java index 5412e079a..6444c967d 100644 --- a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/baskets/BasketNotFoundException.java +++ b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/baskets/BasketNotFoundException.java @@ -1,6 +1,6 @@ package com.dressca.applicationcore.baskets; -import com.dressca.systemcommon.constant.ExceptionIdConstant; +import com.dressca.applicationcore.constant.ExceptionIdConstant; import com.dressca.systemcommon.exception.LogicException; /** @@ -8,7 +8,7 @@ */ public class BasketNotFoundException extends LogicException { public BasketNotFoundException(long basketId) { - super(null, ExceptionIdConstant.E_BASKET0001, new String[] { String.valueOf(basketId) }, + super(null, ExceptionIdConstant.E_BASKET_IS_NULL_ON_CHECKOUT, new String[] { String.valueOf(basketId) }, new String[] { String.valueOf(basketId) }); } } diff --git a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/baskets/CatalogItemInBasketNotFoundException.java b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/baskets/CatalogItemInBasketNotFoundException.java index 7cd970d0a..798c299b6 100644 --- a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/baskets/CatalogItemInBasketNotFoundException.java +++ b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/baskets/CatalogItemInBasketNotFoundException.java @@ -2,7 +2,7 @@ import java.util.List; import java.util.StringJoiner; -import com.dressca.systemcommon.constant.ExceptionIdConstant; +import com.dressca.applicationcore.constant.ExceptionIdConstant; import com.dressca.systemcommon.exception.LogicException; /** @@ -17,7 +17,7 @@ public class CatalogItemInBasketNotFoundException extends LogicException { * @param basketId 買い物かごID */ public CatalogItemInBasketNotFoundException(List catalogIds, long basketId) { - super(null, ExceptionIdConstant.E_BASKET0002, + super(null, ExceptionIdConstant.E_CATALOG_ITEM_ID_DOES_EXIST_IN_BASKET, new String[] { String.valueOf(basketId), convertCatalogIds(catalogIds) }, new String[] { String.valueOf(basketId), convertCatalogIds(catalogIds) }); } diff --git a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/catalog/CatalogNotFoundException.java b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/catalog/CatalogNotFoundException.java index 9dc370518..cd9679724 100644 --- a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/catalog/CatalogNotFoundException.java +++ b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/catalog/CatalogNotFoundException.java @@ -1,6 +1,6 @@ package com.dressca.applicationcore.catalog; -import com.dressca.systemcommon.constant.ExceptionIdConstant; +import com.dressca.applicationcore.constant.ExceptionIdConstant; import com.dressca.systemcommon.exception.LogicException; /** @@ -8,11 +8,11 @@ */ public class CatalogNotFoundException extends LogicException { public CatalogNotFoundException(long catalogId) { - super(null, ExceptionIdConstant.E_CATALOG0001, new String[] { String.valueOf(catalogId) }, + super(null, ExceptionIdConstant.E_CATALOG_ID_NOT_FOUND, new String[] { String.valueOf(catalogId) }, new String[] { String.valueOf(catalogId) }); } public CatalogNotFoundException() { - super(null, ExceptionIdConstant.E_CATALOG0001, null, null); + super(null, ExceptionIdConstant.E_CATALOG_ID_NOT_FOUND, null, null); } } \ No newline at end of file diff --git a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/constant/ExceptionIdConstant.java b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/constant/ExceptionIdConstant.java new file mode 100644 index 000000000..05beb283a --- /dev/null +++ b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/constant/ExceptionIdConstant.java @@ -0,0 +1,25 @@ +package com.dressca.applicationcore.constant; + +/** + * 業務例外ID用定数クラス。 + */ +public class ExceptionIdConstant { + + /** 存在しないアセットコード: {0} のアセットが要求されました。 */ + public static final String E_ASSET_NOT_FOUND = "assetNotFound"; + + /** 存在しない買い物かごID: {0} の買い物かごが要求されました。 */ + public static final String E_BASKET_IS_NULL_ON_CHECKOUT = "basketIsNullOnCheckout"; + + /** 買い物かごID: {0} に商品ID: {1} の商品が見つかりませんでした。 */ + public static final String E_CATALOG_ITEM_ID_DOES_EXIST_IN_BASKET = "catalogItemIdDoesNotExistInBasket"; + + /** 注文のチェックアウト時に買い物かごが空でした。 */ + public static final String E_BASKET_IS_EMPTY_ON_CHECKOUT = "basketIsEmptyOnCheckout"; + + /** 存在しない注文情報(注文ID: {0}, 購入者ID: {1})が要求されました。 */ + public static final String E_ORDER_NOT_FOUND = "orderNotFound"; + + /** 商品ID: {0} の商品が見つかりませんでした。 */ + public static final String E_CATALOG_ID_NOT_FOUND = "catalogIdNotFound"; +} diff --git a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/constant/MessageIdConstant.java b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/constant/MessageIdConstant.java new file mode 100644 index 000000000..d3b20bd39 --- /dev/null +++ b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/constant/MessageIdConstant.java @@ -0,0 +1,37 @@ +package com.dressca.applicationcore.constant; + +/** + * 業務メッセージID用定数クラス。 + */ +public class MessageIdConstant { + + /** アセット情報{0}を取得します。 */ + public static final String D_ASSET_GET_ASSET = "assetApplicationServiceGetAsset"; + + /** 買い物かごに商品(顧客ID: {0}, カタログ商品ID: {1}, 数量: {2})を追加します。 */ + public static final String D_SHOPPING_ADD_ITEM_TO_BASKET = "shoppingApplicationServiceAddItemToBasket"; + + /** 買い物かごの商品の数量(顧客ID: {0}, 数量: {1})を設定します。 */ + public static final String D_SHOPPING_SET_BASKET_ITEMS_QUANTITIES = "shoppingApplicationServiceSetBasketItemsQuantities"; + + /** 顧客(顧客ID: {0})の買い物かごと情報とその商品一覧を取得します。 */ + public static final String D_SHOPPING_GET_BASKET_ITEMS = "shoppingApplicationServiceGetBasketItems"; + + /** 注文(顧客ID: {0}, お届け先: {1})を確定します。 */ + public static final String D_SHOPPING_CHECKOUT = "shoppingApplicationServiceCheckout"; + + /** 指定した注文ID: {0}, 購入者ID: {1} の注文情報を取得します。 */ + public static final String D_ORDER_GET_ORDER = "orderApplicationServiceGetOrder"; + + /** 条件(ブランドID: {0}, カテゴリID: {1}, ページ: {2}, ページサイズ: {3})に一致するカタログ情報を取得します。 */ + public static final String D_CATALOG_GET_CATALOG_ITEMS = "catalogApplicationServiceGetCatalogItems"; + + /** 条件(ブランドID: {0}, カテゴリID: {1})に一致するカテゴリの件数を取得します。 */ + public static final String D_CATALOG_COUNT_CATALOG_ITEMS = "catalogApplicationServiceCountCatalogItems"; + + /** フィルタリング用のカタログブランドリストを取得します。 */ + public static final String D_CATALOG_GET_BRANDS = "catalogApplicationServiceGetBrands"; + + /** フィルタリング用のカタログカテゴリリストを取得します。 */ + public static final String D_CATALOG_GET_CATEGORIES = "catalogApplicationServiceGetCategories"; +} diff --git a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/order/EmptyBasketOnCheckoutException.java b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/order/EmptyBasketOnCheckoutException.java index 8e52c079c..c11c5d8e3 100644 --- a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/order/EmptyBasketOnCheckoutException.java +++ b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/order/EmptyBasketOnCheckoutException.java @@ -1,6 +1,6 @@ package com.dressca.applicationcore.order; -import com.dressca.systemcommon.constant.ExceptionIdConstant; +import com.dressca.applicationcore.constant.ExceptionIdConstant; import com.dressca.systemcommon.exception.LogicException; /** @@ -9,7 +9,7 @@ public class EmptyBasketOnCheckoutException extends LogicException { public EmptyBasketOnCheckoutException(Throwable cause) { - super(cause, ExceptionIdConstant.E_ORDER0001, null, null); + super(cause, ExceptionIdConstant.E_BASKET_IS_EMPTY_ON_CHECKOUT, null, null); } } diff --git a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/order/OrderNotFoundException.java b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/order/OrderNotFoundException.java index db1183b7c..54305b5c8 100644 --- a/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/order/OrderNotFoundException.java +++ b/samples/web-csr/dressca-backend/application-core/src/main/java/com/dressca/applicationcore/order/OrderNotFoundException.java @@ -1,6 +1,6 @@ package com.dressca.applicationcore.order; -import com.dressca.systemcommon.constant.ExceptionIdConstant; +import com.dressca.applicationcore.constant.ExceptionIdConstant; import com.dressca.systemcommon.exception.LogicException; /** @@ -17,7 +17,7 @@ public class OrderNotFoundException extends LogicException { * @param buyerId 見つからなかった購入者 Id. */ public OrderNotFoundException(Throwable cause, long orderId, String buyerId) { - super(cause, ExceptionIdConstant.E_ORDER0002, new String[] { String.valueOf(orderId), buyerId }, + super(cause, ExceptionIdConstant.E_ORDER_NOT_FOUND, new String[] { String.valueOf(orderId), buyerId }, new String[] { String.valueOf(orderId), buyerId }); } diff --git a/samples/web-csr/dressca-backend/application-core/src/main/resources/application-common.properties b/samples/web-csr/dressca-backend/application-core/src/main/resources/application-common.properties index 8b1378917..bdb28b33e 100644 --- a/samples/web-csr/dressca-backend/application-core/src/main/resources/application-common.properties +++ b/samples/web-csr/dressca-backend/application-core/src/main/resources/application-common.properties @@ -1 +1,3 @@ - +# messages.properties +spring.messages.basename=applicationcore.messages +spring.messages.encoding=UTF-8 \ No newline at end of file diff --git a/samples/web-csr/dressca-backend/application-core/src/main/resources/applicationcore/messages.properties b/samples/web-csr/dressca-backend/application-core/src/main/resources/applicationcore/messages.properties new file mode 100644 index 000000000..ca05edab3 --- /dev/null +++ b/samples/web-csr/dressca-backend/application-core/src/main/resources/applicationcore/messages.properties @@ -0,0 +1,18 @@ +# エラーメッセージ +assetNotFound=存在しないアセットコード: {0} のアセットが要求されました。 +basketIsNullOnCheckout=存在しない買い物かごID: {0} の買い物かごが要求されました。 +catalogItemIdDoesNotExistInBasket=買い物かごID: {0} に商品ID: {1} の商品が見つかりませんでした。 +basketIsEmptyOnCheckout=注文のチェックアウト時に買い物かごが空でした。 +orderNotFound=存在しない注文情報(注文ID: {0}, 購入者ID: {1})が要求されました。 +catalogIdNotFound=商品ID: {0} の商品が見つかりませんでした。 +# 通知用メッセージ +assetApplicationServiceGetAsset=アセット情報{0}を取得します。 +shoppingApplicationServiceAddItemToBasket=買い物かごに商品(顧客ID: {0}, カタログ商品ID: {1}, 数量: {2})を追加します。 +shoppingApplicationServiceSetBasketItemsQuantities=買い物かごの商品の数量(顧客ID: {0}, 数量: {1})を設定します。 +shoppingApplicationServiceGetBasketItems=顧客(顧客ID: {0})の買い物かごと情報とその商品一覧を取得します。 +shoppingApplicationServiceCheckout=注文(顧客ID: {0}, お届け先: {1})を確定します。 +orderApplicationServiceGetOrder=指定した注文ID: {0}, 購入者ID: {1} の注文情報を取得します。 +catalogApplicationServiceGetCatalogItems=条件(ブランドID: {0}, カテゴリID: {1}, ページ: {2}, ページサイズ: {3})に一致するカタログ情報を取得します。 +catalogApplicationServiceCountCatalogItems=条件(ブランドID: {0}, カテゴリID: {1})に一致するカテゴリの件数を取得します。 +catalogApplicationServiceGetBrands=フィルタリング用のカタログブランドリストを取得します。 +catalogApplicationServiceGetCategories=フィルタリング用のカタログカテゴリリストを取得します。 \ No newline at end of file diff --git a/samples/web-csr/dressca-backend/application-core/src/test/java/com/dressca/applicationcore/applicationservice/AssetApplicationServiceTest.java b/samples/web-csr/dressca-backend/application-core/src/test/java/com/dressca/applicationcore/applicationservice/AssetApplicationServiceTest.java index 7b263b017..b3c79c770 100644 --- a/samples/web-csr/dressca-backend/application-core/src/test/java/com/dressca/applicationcore/applicationservice/AssetApplicationServiceTest.java +++ b/samples/web-csr/dressca-backend/application-core/src/test/java/com/dressca/applicationcore/applicationservice/AssetApplicationServiceTest.java @@ -18,6 +18,7 @@ import org.springframework.context.MessageSource; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import com.dressca.applicationcore.assets.Asset; import com.dressca.applicationcore.assets.AssetNotFoundException; @@ -29,6 +30,7 @@ * {@link AssetApplicationService}の動作をテストするクラスです。 */ @ExtendWith(SpringExtension.class) +@TestPropertySource(properties = "spring.messages.basename=applicationcore.messages") @ImportAutoConfiguration(MessageSourceAutoConfiguration.class) public class AssetApplicationServiceTest { diff --git a/samples/web-csr/dressca-backend/application-core/src/test/java/com/dressca/applicationcore/applicationservice/CatalogApplicationServiceTest.java b/samples/web-csr/dressca-backend/application-core/src/test/java/com/dressca/applicationcore/applicationservice/CatalogApplicationServiceTest.java index 7ffe8db4b..048901f4b 100644 --- a/samples/web-csr/dressca-backend/application-core/src/test/java/com/dressca/applicationcore/applicationservice/CatalogApplicationServiceTest.java +++ b/samples/web-csr/dressca-backend/application-core/src/test/java/com/dressca/applicationcore/applicationservice/CatalogApplicationServiceTest.java @@ -17,6 +17,7 @@ import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration; import org.springframework.context.MessageSource; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import com.dressca.applicationcore.catalog.CatalogBrand; import com.dressca.applicationcore.catalog.CatalogBrandRepository; @@ -29,6 +30,7 @@ * {@link CatalogApplicationService}の動作をテストするクラスです。 */ @ExtendWith(SpringExtension.class) +@TestPropertySource(properties = "spring.messages.basename=applicationcore.messages") @ImportAutoConfiguration(MessageSourceAutoConfiguration.class) public class CatalogApplicationServiceTest { @Mock diff --git a/samples/web-csr/dressca-backend/application-core/src/test/java/com/dressca/applicationcore/applicationservice/OrderApplicationServiceTest.java b/samples/web-csr/dressca-backend/application-core/src/test/java/com/dressca/applicationcore/applicationservice/OrderApplicationServiceTest.java index 78ca9cd9c..f6b506d1e 100644 --- a/samples/web-csr/dressca-backend/application-core/src/test/java/com/dressca/applicationcore/applicationservice/OrderApplicationServiceTest.java +++ b/samples/web-csr/dressca-backend/application-core/src/test/java/com/dressca/applicationcore/applicationservice/OrderApplicationServiceTest.java @@ -24,12 +24,14 @@ import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration; import org.springframework.context.MessageSource; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; /** * {@link OrderApplicationService}の動作をテストするクラスです。 */ @ExtendWith(SpringExtension.class) +@TestPropertySource(properties = "spring.messages.basename=applicationcore.messages") @ImportAutoConfiguration(MessageSourceAutoConfiguration.class) public class OrderApplicationServiceTest { @Mock diff --git a/samples/web-csr/dressca-backend/application-core/src/test/java/com/dressca/applicationcore/applicationservice/ShoppingApplicationServiceTest.java b/samples/web-csr/dressca-backend/application-core/src/test/java/com/dressca/applicationcore/applicationservice/ShoppingApplicationServiceTest.java index e3e98c1e3..2033abd6b 100644 --- a/samples/web-csr/dressca-backend/application-core/src/test/java/com/dressca/applicationcore/applicationservice/ShoppingApplicationServiceTest.java +++ b/samples/web-csr/dressca-backend/application-core/src/test/java/com/dressca/applicationcore/applicationservice/ShoppingApplicationServiceTest.java @@ -27,6 +27,7 @@ import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration; import org.springframework.context.MessageSource; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import com.dressca.applicationcore.baskets.Basket; import com.dressca.applicationcore.baskets.BasketNotFoundException; @@ -48,6 +49,7 @@ * {@link ShoppingApplicationService}の動作をテストするクラスです。 */ @ExtendWith(SpringExtension.class) +@TestPropertySource(properties = "spring.messages.basename=applicationcore.messages") @ImportAutoConfiguration(MessageSourceAutoConfiguration.class) public class ShoppingApplicationServiceTest { @Mock diff --git a/samples/web-csr/dressca-backend/batch/build.gradle b/samples/web-csr/dressca-backend/batch/build.gradle index 8d4b8a2f6..08182fb82 100644 --- a/samples/web-csr/dressca-backend/batch/build.gradle +++ b/samples/web-csr/dressca-backend/batch/build.gradle @@ -21,6 +21,7 @@ dependencies { implementation supportDependencies.spring_boot_starter_batch implementation supportDependencies.mybatis_spring_boot_starter implementation supportDependencies.commons_lang3 + implementation supportDependencies.spring_boot_starter_log4j2 implementation project(':application-core') implementation project(':infrastructure') diff --git a/samples/web-csr/dressca-backend/system-common/src/main/resources/log4j2.xml b/samples/web-csr/dressca-backend/batch/src/main/resources/log4j2.xml similarity index 100% rename from samples/web-csr/dressca-backend/system-common/src/main/resources/log4j2.xml rename to samples/web-csr/dressca-backend/batch/src/main/resources/log4j2.xml diff --git a/samples/web-csr/dressca-backend/build.gradle b/samples/web-csr/dressca-backend/build.gradle index 8219c64b5..740529d59 100644 --- a/samples/web-csr/dressca-backend/build.gradle +++ b/samples/web-csr/dressca-backend/build.gradle @@ -6,11 +6,6 @@ */ buildscript { apply from: 'dependencies.gradle' - configurations { - all { - exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' - } - } } plugins { diff --git a/samples/web-csr/dressca-backend/config/checkstyle/suppressions.xml b/samples/web-csr/dressca-backend/config/checkstyle/suppressions.xml index a1a5f6efd..b072ddb60 100644 --- a/samples/web-csr/dressca-backend/config/checkstyle/suppressions.xml +++ b/samples/web-csr/dressca-backend/config/checkstyle/suppressions.xml @@ -9,4 +9,6 @@ + + \ No newline at end of file diff --git a/samples/web-csr/dressca-backend/infrastructure/build.gradle b/samples/web-csr/dressca-backend/infrastructure/build.gradle index e82f74e29..cab6fee6e 100644 --- a/samples/web-csr/dressca-backend/infrastructure/build.gradle +++ b/samples/web-csr/dressca-backend/infrastructure/build.gradle @@ -34,6 +34,13 @@ dependencies { testImplementation supportDependencies.mybatis_spring_boot_starter_test } +configurations { + all { + exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' + } +} + + tasks.named('test') { useJUnitPlatform() } diff --git a/samples/web-csr/dressca-backend/system-common/build.gradle b/samples/web-csr/dressca-backend/system-common/build.gradle index add2aa8c4..9c5663c3e 100644 --- a/samples/web-csr/dressca-backend/system-common/build.gradle +++ b/samples/web-csr/dressca-backend/system-common/build.gradle @@ -19,7 +19,6 @@ repositories { dependencies { implementation supportDependencies.spring_boot_starter - implementation supportDependencies.spring_boot_starter_log4j2 implementation supportDependencies.commons_lang3 testImplementation supportDependencies.spring_boot_starter_test } diff --git a/samples/web-csr/dressca-backend/system-common/src/main/java/com/dressca/systemcommon/constant/CommonExceptionIdConstant.java b/samples/web-csr/dressca-backend/system-common/src/main/java/com/dressca/systemcommon/constant/CommonExceptionIdConstant.java new file mode 100644 index 000000000..634970cd6 --- /dev/null +++ b/samples/web-csr/dressca-backend/system-common/src/main/java/com/dressca/systemcommon/constant/CommonExceptionIdConstant.java @@ -0,0 +1,13 @@ +package com.dressca.systemcommon.constant; + +/** + * 例外ID用定数クラス。 + */ +public class CommonExceptionIdConstant { + /** 想定外のシステムエラー。 */ + public static final String E_SYSTEM = "systemError"; + + /** 想定外の業務エラー。 */ + public static final String E_BUSINESS = "businessError"; + +} diff --git a/samples/web-csr/dressca-backend/system-common/src/main/java/com/dressca/systemcommon/constant/ExceptionIdConstant.java b/samples/web-csr/dressca-backend/system-common/src/main/java/com/dressca/systemcommon/constant/ExceptionIdConstant.java deleted file mode 100644 index fe69d707d..000000000 --- a/samples/web-csr/dressca-backend/system-common/src/main/java/com/dressca/systemcommon/constant/ExceptionIdConstant.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.dressca.systemcommon.constant; - -/** - * 例外ID用定数クラス。 - */ -public class ExceptionIdConstant { - /** 想定外のエラー。 */ - public static final String E_SHARE0000 = "E_SHARE0000"; - - // 業務例外用 - /** アセットが見つからなかった際の例外。 */ - public static final String E_ASSET0001 = "E_ASSET0001"; - - /** 買い物かごが見つからなかった際の例外。 */ - public static final String E_BASKET0001 = "E_BASKET0001"; - - /** 買い物かご内に想定した注文情報が存在しなかった際の例外。 */ - public static final String E_BASKET0002 = "E_BASKET0002"; - - /** 注文のチェックアウト処理開始時に買い物かごが空だった際の例外。 */ - public static final String E_ORDER0001 = "E_ORDER0001"; - - /** 注文情報が存在しなかった際の例外。 */ - public static final String E_ORDER0002 = "E_ORDER0002"; - - /** カタログ情報が存在しなかった際の例外。 */ - public static final String E_CATALOG0001 = "E_CATALOG0002"; - -} diff --git a/samples/web-csr/dressca-backend/system-common/src/main/java/com/dressca/systemcommon/constant/MessageIdConstant.java b/samples/web-csr/dressca-backend/system-common/src/main/java/com/dressca/systemcommon/constant/MessageIdConstant.java deleted file mode 100644 index 4d57df26f..000000000 --- a/samples/web-csr/dressca-backend/system-common/src/main/java/com/dressca/systemcommon/constant/MessageIdConstant.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.dressca.systemcommon.constant; - -/** - * メッセージID用定数クラス。 - */ -public class MessageIdConstant { - - // システムエラーメッセージ用 - /** 想定外のエラーが発生しました。 */ - public static final String E_SHARE0000_FRONT = "F_SHARE0000.front"; - public static final String E_SHARE0000_LOG = "F_SHARE0000.log"; - - // 業務エラーメッセージ用 - /** アセットコード: {0} のアセットが見つかりませんでした。 */ - public static final String E_ASSET0001_FRONT = "E_ASSET0001.front"; - public static final String E_ASSET0001_LOG = "E_ASSET0001.log"; - - /** 買い物かごID: {0} の買い物かごが見つかりませんでした。 */ - public static final String E_BASKET0001_FRONT = "E_BASKET0001.front"; - public static final String E_BASKET0001_LOG = "E_BASKET0001.log"; - - /** 注文のチェックアウト時に買い物かごが空でした。 */ - public static final String E_ORDER0001_FRONT = "E_ORDER0001.front"; - public static final String E_ORDER0001_LOG = "E_ORDER0001.log"; - - /** 注文ID: {0}, 購入者ID: {1} に該当する注文情報が見つかりませんでした。 */ - public static final String E_ORDER0002_FRONT = "E_ORDER0002.front"; - public static final String E_ORDER0002_LOG = "E_ORDER0002.log"; - - /** アセット情報{0}を取得します。 */ - public static final String D_ASSET0001_LOG = "D_ASSET0001.log"; - - /** 買い物かごに商品(顧客ID: {0}, カタログ商品ID: {1}, 数量: {2})を追加します。 */ - public static final String D_BASKET0001_LOG = "D_BASKET0001.log"; - - /** 買い物かごの商品の数量(顧客ID: {0}, 数量: {1})を設定します。 */ - public static final String D_BASKET0002_LOG = "D_BASKET0002.log"; - - /** 顧客(顧客ID: {0})の買い物かごと情報とその商品一覧を取得します。 */ - public static final String D_BASKET0003_LOG = "D_BASKET0003.log"; - - /** 注文(顧客ID: {0}, お届け先: {1})を確定します。 */ - public static final String D_BASKET0004_LOG = "D_BASKET0004.log"; - - /** 指定した注文ID: {0}, 購入者ID: {1} の注文情報を取得します。 */ - public static final String D_ORDER0001_LOG = "D_ORDER0001.log"; - - /** 条件(ブランドID: {0}, カテゴリID: {1}, ページ: {2}, ページサイズ: {3})に一致するカタログ情報を取得します。 */ - public static final String D_CATALOG0001_LOG = "D_CATALOG0001.log"; - - /** 条件(ブランドID: {0}, カテゴリID: {1})に一致するカテゴリの件数を取得します。 */ - public static final String D_CATALOG0002_LOG = "D_CATALOG0002.log"; - - /** フィルタリング用のカタログブランドリストを取得します。 */ - public static final String D_CATALOG0003_LOG = "D_CATALOG0003.log"; - - /** フィルタリング用のカタログカテゴリリストを取得します。 */ - public static final String D_CATALOG0004_LOG = "D_CATALOG0004.log"; -} diff --git a/samples/web-csr/dressca-backend/system-common/src/main/java/com/dressca/systemcommon/exception/response/ErrorResponse.java b/samples/web-csr/dressca-backend/system-common/src/main/java/com/dressca/systemcommon/exception/response/ErrorResponse.java deleted file mode 100644 index de99dcb16..000000000 --- a/samples/web-csr/dressca-backend/system-common/src/main/java/com/dressca/systemcommon/exception/response/ErrorResponse.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.dressca.systemcommon.exception.response; - -import java.util.Locale; -import com.dressca.systemcommon.exception.LogicException; -import com.dressca.systemcommon.exception.SystemException; -import com.dressca.systemcommon.util.ApplicationContextWrapper; -import org.springframework.context.MessageSource; -import lombok.Getter; -import lombok.Setter; - -/** - * エラーレスポンス。 フロントへのレスポンスボディにJSONでマッピングされるエラー情報のクラス - */ -@Getter -@Setter -public class ErrorResponse { - /** - * エラーコード。 - */ - private String type; - - /** エラーコードに対応するコメント。 */ - private String title; - - /** エラーメッセージ。 */ - private String detail; - - /** 項目チェックエラー用。 */ - // private Map> invalidParams; - - /** リクエストURI。 */ - private String instance; - - /** メッセージソース。 */ - private static MessageSource messageSource = (MessageSource) ApplicationContextWrapper.getBean(MessageSource.class); - - private static final String EXCEPTION_MESSAGE_SUFFIX_FRONT = "front"; - private static final String PROPERTY_DELIMITER = "."; - - /** システム例外用コンストラクタ。 */ - public ErrorResponse(SystemException e, String uri) { - this.type = e.getExceptionId(); - this.title = e.getExceptionId(); - this.detail = getFrontErrorMessage(e.getExceptionId(), e.getFrontMessageValue()); - this.instance = uri; - } - - /** 業務例外用コンストラクタ。 */ - public ErrorResponse(LogicException e, String uri) { - this.type = e.getExceptionId(); - this.title = e.getExceptionId(); - this.detail = getFrontErrorMessage(e.getExceptionId(), e.getFrontMessageValue()); - this.instance = uri; - } - - private String getFrontErrorMessage(String exceptionId, String[] frontMessageValue) { - String code = String.join(PROPERTY_DELIMITER, exceptionId, EXCEPTION_MESSAGE_SUFFIX_FRONT); - return messageSource.getMessage(code, frontMessageValue, Locale.getDefault()); - } -} diff --git a/samples/web-csr/dressca-backend/system-common/src/main/resources/application-common.properties b/samples/web-csr/dressca-backend/system-common/src/main/resources/application-common.properties index 9e0815194..8382ded52 100644 --- a/samples/web-csr/dressca-backend/system-common/src/main/resources/application-common.properties +++ b/samples/web-csr/dressca-backend/system-common/src/main/resources/application-common.properties @@ -1,2 +1,2 @@ -spring.messages.basename=messages +spring.messages.basename=systemcommon.messages spring.messages.encoding=UTF-8 diff --git a/samples/web-csr/dressca-backend/system-common/src/main/resources/messages.properties b/samples/web-csr/dressca-backend/system-common/src/main/resources/messages.properties deleted file mode 100644 index 453a11f01..000000000 --- a/samples/web-csr/dressca-backend/system-common/src/main/resources/messages.properties +++ /dev/null @@ -1,24 +0,0 @@ -E_SHARE0000.front=想定外のエラーが発生しました -E_SHARE0000.log=想定外のエラーが発生しました -E_ASSET0001.front=アセットコード: {0} のアセットが見つかりませんでした。 -E_ASSET0001.log=存在しないアセットコード: {0} のアセットが要求されました。 -E_BASKET0001.front=買い物かごID: {0} の買い物かごが見つかりませんでした。 -E_BASKET0001.log=存在しない買い物かごID: {0} の買い物かごが要求されました。 -E_BASKET0002.front=買い物かごID: {0} に商品ID: {1} の商品が見つかりませんでした。 -E_BASKET0002.log=買い物かごID: {0} に商品ID: {1} の商品が見つかりませんでした。 -E_ORDER0001.front=注文のチェックアウト時に買い物かごが空でした。 -E_ORDER0001.log=注文のチェックアウト時に買い物かごが空でした。 -E_ORDER0002.front=注文ID: {0}, 購入者ID: {1} に該当する注文情報が見つかりませんでした。 -E_ORDER0002.log=存在しない注文情報(注文ID: {0}, 購入者ID: {1})が要求されました。 -E_CATALOG0001.front=商品ID: {0} の商品が見つかりませんでした。 -E_CATALOG0001.log=商品ID: {0} の商品が見つかりませんでした。 -D_ASSET0001.log=アセット情報{0}を取得します。 -D_BASKET0001.log=買い物かごに商品(顧客ID: {0}, カタログ商品ID: {1}, 数量: {2})を追加します。 -D_BASKET0002.log=買い物かごの商品の数量(顧客ID: {0}, 数量: {1})を設定します。 -D_BASKET0003.log=顧客(顧客ID: {0})の買い物かごと情報とその商品一覧を取得します。 -D_BASKET0004.log=注文(顧客ID: {0}, お届け先: {1})を確定します。 -D_ORDER0001.log=指定した注文ID: {0}, 購入者ID: {1} の注文情報を取得します。 -D_CATALOG0001.log=条件(ブランドID: {0}, カテゴリID: {1}, ページ: {2}, ページサイズ: {3})に一致するカタログ情報を取得します。 -D_CATALOG0002.log=条件(ブランドID: {0}, カテゴリID: {1})に一致するカテゴリの件数を取得します。 -D_CATALOG0003.log=フィルタリング用のカタログブランドリストを取得します。 -D_CATALOG0004.log=フィルタリング用のカタログカテゴリリストを取得します。 \ No newline at end of file diff --git a/samples/web-csr/dressca-backend/system-common/src/main/resources/systemcommon/messages.properties b/samples/web-csr/dressca-backend/system-common/src/main/resources/systemcommon/messages.properties new file mode 100644 index 000000000..c9fc1c4bb --- /dev/null +++ b/samples/web-csr/dressca-backend/system-common/src/main/resources/systemcommon/messages.properties @@ -0,0 +1,2 @@ +systemError=想定外のシステムエラーが発生しました +businessError=想定外の業務エラーが発生しました \ No newline at end of file diff --git a/samples/web-csr/dressca-backend/web/build.gradle b/samples/web-csr/dressca-backend/web/build.gradle index b3720d4ac..8963f8cae 100644 --- a/samples/web-csr/dressca-backend/web/build.gradle +++ b/samples/web-csr/dressca-backend/web/build.gradle @@ -25,13 +25,13 @@ dependencies { implementation supportDependencies.springdoc_openapi_starter_webmvc_ui implementation supportDependencies.commons_lang3 implementation supportDependencies.spring_boot_security_starter - + implementation supportDependencies.spring_boot_starter_log4j2 + implementation project(':application-core') implementation project(':infrastructure') implementation project(':system-common') testImplementation supportDependencies.spring_boot_starter_test - testImplementation supportDependencies.spring_boot_starter_log4j2 testImplementation supportDependencies.spring_security_test compileOnly supportDependencies.servlet_api @@ -55,5 +55,12 @@ openApi { tasks.named('test') { useJUnitPlatform() } +tasks.register('bootRunDev', org.springframework.boot.gradle.tasks.run.BootRun){ + group = 'application' + description = 'Runs this project as a Spring Boot Application using local profile.' + args = ["--spring.profiles.active=local"] + main = 'com.dressca.web.WebApplication' + classpath = sourceSets.main.runtimeClasspath +} build.dependsOn("generateOpenApiDocs") \ No newline at end of file diff --git a/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/constant/ProblemDetailsConstant.java b/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/constant/ProblemDetailsConstant.java index e30d889d1..1f6b53aa7 100644 --- a/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/constant/ProblemDetailsConstant.java +++ b/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/constant/ProblemDetailsConstant.java @@ -11,6 +11,9 @@ public class ProblemDetailsConstant { /** システムエラーのタイトル。 */ public static final String SYSTEM_ERROR_TITLE = "システムエラー"; - /** errorキー。 */ - public static final String ERROR_KEY = "error"; + /** Exception ID。 */ + public static final String EXCEPTION_ID = "exceptionId"; + + /** Exception ID に紐づくメッセージ。 */ + public static final String EXCEPTION_VALUES = "exceptionValues"; } diff --git a/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/AssetsController.java b/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/AssetsController.java index 6dd177d26..5e4abae70 100644 --- a/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/AssetsController.java +++ b/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/AssetsController.java @@ -7,12 +7,16 @@ import com.dressca.applicationcore.assets.AssetTypes; import com.dressca.systemcommon.constant.SystemPropertyConstants; import com.dressca.systemcommon.exception.LogicException; +import com.dressca.web.controller.advice.ProblemDetailsCreation; +import com.dressca.web.log.ErrorMessageBuilder; import org.apache.commons.lang3.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ProblemDetail; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -39,6 +43,9 @@ public class AssetsController { @Autowired private AssetApplicationService service; + @Autowired + private ProblemDetailsCreation problemDetailsCreation; + private static final Logger apLog = LoggerFactory.getLogger(SystemPropertyConstants.APPLICATION_LOG_LOGGER); /** @@ -52,7 +59,7 @@ public class AssetsController { @ApiResponse(responseCode = "200", description = "成功.", content = @Content(mediaType = "image/*", schema = @Schema(implementation = Resource.class))), @ApiResponse(responseCode = "404", description = "アセットコードに対応するアセットがない.", content = @Content) }) @GetMapping("{assetCode}") - public ResponseEntity get( + public ResponseEntity get( @Parameter(required = true, description = "アセットコード") @PathVariable("assetCode") String assetCode) throws LogicException { try { @@ -63,7 +70,15 @@ public ResponseEntity get( } catch (AssetNotFoundException e) { apLog.info(e.getMessage()); apLog.debug(ExceptionUtils.getStackTrace(e)); - return ResponseEntity.notFound().build(); + ErrorMessageBuilder errorBuilder = new ErrorMessageBuilder(e, e.getExceptionId(), + e.getLogMessageValue(), e.getFrontMessageValue()); + ProblemDetail problemDetail = problemDetailsCreation.createProblemDetail( + errorBuilder, + e.getExceptionId(), + HttpStatus.NOT_FOUND); + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .contentType(MediaType.APPLICATION_JSON) + .body(problemDetail); } } diff --git a/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/BasketItemController.java b/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/BasketItemController.java index 2c1dc33fc..d62380f7a 100644 --- a/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/BasketItemController.java +++ b/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/BasketItemController.java @@ -7,11 +7,13 @@ import com.dressca.applicationcore.baskets.CatalogItemInBasketNotFoundException; import com.dressca.applicationcore.catalog.CatalogItem; import com.dressca.applicationcore.catalog.CatalogNotFoundException; +import com.dressca.web.controller.advice.ProblemDetailsCreation; import com.dressca.web.controller.dto.baskets.BasketItemResponse; import com.dressca.web.controller.dto.baskets.BasketResponse; import com.dressca.web.controller.dto.baskets.PostBasketItemsRequest; import com.dressca.web.controller.dto.baskets.PutBasketItemsRequest; import com.dressca.web.controller.dto.catalog.CatalogItemSummaryResponse; +import com.dressca.web.log.ErrorMessageBuilder; import com.dressca.web.mapper.BasketMapper; import com.dressca.web.mapper.CatalogItemSummaryMapper; import io.swagger.v3.oas.annotations.Operation; @@ -27,6 +29,9 @@ import jakarta.servlet.http.HttpServletRequest; import lombok.AllArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ProblemDetail; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -49,6 +54,9 @@ public class BasketItemController { @Autowired private ShoppingApplicationService shoppingApplicationService; + @Autowired + private ProblemDetailsCreation problemDetailsCreation; + /** * 買い物かごアイテムの一覧を取得します。 * @@ -102,8 +110,27 @@ public ResponseEntity putBasketItems(@RequestBody List try { shoppingApplicationService.setQuantities(buyerId, quantities); - } catch (CatalogNotFoundException | CatalogItemInBasketNotFoundException e) { - return ResponseEntity.badRequest().build(); + } catch (CatalogNotFoundException e) { + ErrorMessageBuilder errorBuilder = new ErrorMessageBuilder(e, + e.getExceptionId(), + e.getLogMessageValue(), e.getFrontMessageValue()); + ProblemDetail problemDetail = problemDetailsCreation.createProblemDetail( + errorBuilder, + e.getExceptionId(), + HttpStatus.BAD_REQUEST); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .contentType(MediaType.APPLICATION_JSON) + .body(problemDetail); + } catch (CatalogItemInBasketNotFoundException e) { + ErrorMessageBuilder errorBuilder = new ErrorMessageBuilder(e, + e.getExceptionId(), e.getLogMessageValue(), e.getFrontMessageValue()); + ProblemDetail problemDetail = problemDetailsCreation.createProblemDetail( + errorBuilder, + e.getExceptionId(), + HttpStatus.BAD_REQUEST); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .contentType(MediaType.APPLICATION_JSON) + .body(problemDetail); } return ResponseEntity.noContent().build(); } @@ -142,7 +169,13 @@ public ResponseEntity postBasketItem(@RequestBody PostBasketItemsRequest post postBasketItem.getCatalogItemId(), postBasketItem.getAddedQuantity()); } catch (CatalogNotFoundException e) { - return ResponseEntity.badRequest().build(); + ErrorMessageBuilder errorBuilder = new ErrorMessageBuilder(e, + e.getExceptionId(), e.getLogMessageValue(), e.getFrontMessageValue()); + ProblemDetail problemDetail = problemDetailsCreation.createProblemDetail( + errorBuilder, e.getExceptionId(), HttpStatus.BAD_REQUEST); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .contentType(MediaType.APPLICATION_JSON) + .body(problemDetail); } return ResponseEntity.created(URI.create("/basket-items")).build(); } @@ -174,9 +207,21 @@ public ResponseEntity deleteBasketItem(@PathVariable("catalogItemId") long ca try { this.shoppingApplicationService.setQuantities(buyerId, Map.of(catalogItemId, 0)); } catch (CatalogNotFoundException e) { - return ResponseEntity.badRequest().build(); + ErrorMessageBuilder errorBuilder = new ErrorMessageBuilder(e, + e.getExceptionId(), e.getLogMessageValue(), e.getFrontMessageValue()); + ProblemDetail problemDetail = problemDetailsCreation.createProblemDetail( + errorBuilder, e.getExceptionId(), HttpStatus.BAD_REQUEST); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .contentType(MediaType.APPLICATION_JSON) + .body(problemDetail); } catch (CatalogItemInBasketNotFoundException e) { - return ResponseEntity.notFound().build(); + ErrorMessageBuilder errorBuilder = new ErrorMessageBuilder(e, + e.getExceptionId(), e.getLogMessageValue(), e.getFrontMessageValue()); + ProblemDetail problemDetail = problemDetailsCreation.createProblemDetail( + errorBuilder, e.getExceptionId(), HttpStatus.NOT_FOUND); + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .contentType(MediaType.APPLICATION_JSON) + .body(problemDetail); } return ResponseEntity.noContent().build(); } diff --git a/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/OrderController.java b/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/OrderController.java index 9822ba95d..987e91c08 100644 --- a/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/OrderController.java +++ b/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/OrderController.java @@ -7,11 +7,13 @@ import com.dressca.applicationcore.order.Order; import com.dressca.applicationcore.order.OrderNotFoundException; import com.dressca.applicationcore.order.ShipTo; -import com.dressca.systemcommon.constant.ExceptionIdConstant; +import com.dressca.systemcommon.constant.CommonExceptionIdConstant; import com.dressca.systemcommon.constant.SystemPropertyConstants; import com.dressca.systemcommon.exception.SystemException; +import com.dressca.web.controller.advice.ProblemDetailsCreation; import com.dressca.web.controller.dto.order.OrderResponse; import com.dressca.web.controller.dto.order.PostOrderRequest; +import com.dressca.web.log.ErrorMessageBuilder; import com.dressca.web.mapper.OrderMapper; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; @@ -27,6 +29,9 @@ import org.slf4j.LoggerFactory; import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ProblemDetail; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -49,6 +54,9 @@ public class OrderController { @Autowired private ShoppingApplicationService shoppingApplicationService; + @Autowired + private ProblemDetailsCreation problemDetailsCreation; + private static final Logger apLog = LoggerFactory.getLogger(SystemPropertyConstants.APPLICATION_LOG_LOGGER); /** @@ -62,7 +70,7 @@ public class OrderController { @ApiResponse(responseCode = "200", description = "成功.", content = @Content(mediaType = "application/json", schema = @Schema(implementation = OrderResponse.class))), @ApiResponse(responseCode = "404", description = "注文IDが存在しない.", content = @Content) }) @GetMapping("{orderId}") - public ResponseEntity getById(@PathVariable("orderId") long orderId, + public ResponseEntity getById(@PathVariable("orderId") long orderId, HttpServletRequest req) { String buyerId = req.getAttribute("buyerId").toString(); @@ -73,7 +81,16 @@ public ResponseEntity getById(@PathVariable("orderId") long order } catch (OrderNotFoundException e) { apLog.info(e.getMessage()); apLog.debug(ExceptionUtils.getStackTrace(e)); - return ResponseEntity.notFound().build(); + ErrorMessageBuilder errorBuilder = new ErrorMessageBuilder(e, + e.getExceptionId(), + e.getLogMessageValue(), e.getFrontMessageValue()); + ProblemDetail problemDetail = problemDetailsCreation.createProblemDetail( + errorBuilder, + e.getExceptionId(), + HttpStatus.NOT_FOUND); + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .contentType(MediaType.APPLICATION_JSON) + .body(problemDetail); } } @@ -99,7 +116,7 @@ public ResponseEntity postOrder(@RequestBody @Valid PostOrderRequest postOrde order = shoppingApplicationService.checkout(buyerId, shipToAddress); } catch (EmptyBasketOnCheckoutException e) { // ここでは発生しえないので、システムエラーとする - throw new SystemException(e, ExceptionIdConstant.E_SHARE0000, null, null); + throw new SystemException(e, CommonExceptionIdConstant.E_SYSTEM, null, null); } String requestUri = req.getRequestURL().toString(); diff --git a/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/advice/ExceptionHandlerControllerAdvice.java b/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/advice/ExceptionHandlerControllerAdvice.java index 27b12130e..932bbe879 100644 --- a/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/advice/ExceptionHandlerControllerAdvice.java +++ b/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/advice/ExceptionHandlerControllerAdvice.java @@ -1,15 +1,15 @@ package com.dressca.web.controller.advice; import jakarta.servlet.http.HttpServletRequest; -import com.dressca.systemcommon.constant.ExceptionIdConstant; +import com.dressca.systemcommon.constant.CommonExceptionIdConstant; import com.dressca.systemcommon.constant.SystemPropertyConstants; import com.dressca.systemcommon.exception.LogicException; import com.dressca.systemcommon.exception.SystemException; import com.dressca.web.constant.ProblemDetailsConstant; import com.dressca.web.log.ErrorMessageBuilder; -import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ProblemDetail; @@ -17,19 +17,20 @@ import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; -import org.springframework.context.annotation.Profile; /** - * サーバーエラーのハンドリングを行うクラスです(本番環境用)。 + * サーバーエラーのハンドリングを行うクラスです。 */ -@ControllerAdvice -@Profile("production | test") +@ControllerAdvice(basePackages = "com.dressca") public class ExceptionHandlerControllerAdvice extends ResponseEntityExceptionHandler { private static final Logger apLog = LoggerFactory.getLogger(SystemPropertyConstants.APPLICATION_LOG_LOGGER); + @Autowired + private ProblemDetailsCreation problemDetailsCreation; + /** - * その他の業務エラーをステータースコード500で返却する(本番環境、テスト環境用)。 + * その他の業務エラーをステータースコード500で返却する。 * * @param e 業務例外 * @param req リクエスト @@ -37,35 +38,37 @@ public class ExceptionHandlerControllerAdvice extends ResponseEntityExceptionHan */ @ExceptionHandler(LogicException.class) public ResponseEntity handleLogicException(LogicException e, HttpServletRequest req) { - ErrorMessageBuilder errorBuilder = new ErrorMessageBuilder(e, e.getExceptionId(), e.getLogMessageValue(), - e.getFrontMessageValue()); + ErrorMessageBuilder errorBuilder = new ErrorMessageBuilder(e, CommonExceptionIdConstant.E_BUSINESS, null, null); apLog.error(errorBuilder.createLogMessageStackTrace()); - ProblemDetail problemDetail = createProblemDetail(errorBuilder, ProblemDetailsConstant.LOGIC_ERROR_TITLE); + ProblemDetail problemDetail = problemDetailsCreation.createProblemDetail(errorBuilder, + ProblemDetailsConstant.LOGIC_ERROR_TITLE, + HttpStatus.INTERNAL_SERVER_ERROR); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .contentType(MediaType.APPLICATION_JSON) .body(problemDetail); } /** - * その他のシステムエラーをステータースコード500で返却する(本番環境、テスト環境用)。 + * その他のシステムエラーをステータースコード500で返却する。 * * @param e その他の例外 * @param req リクエスト * @return ステータースコード500のレスポンス */ @ExceptionHandler(SystemException.class) - public ResponseEntity handleException(SystemException e, HttpServletRequest req) { - ErrorMessageBuilder errorBuilder = new ErrorMessageBuilder(e, e.getExceptionId(), e.getLogMessageValue(), - e.getFrontMessageValue()); + public ResponseEntity handleSystemException(SystemException e, HttpServletRequest req) { + ErrorMessageBuilder errorBuilder = new ErrorMessageBuilder(e, CommonExceptionIdConstant.E_SYSTEM, null, null); apLog.error(errorBuilder.createLogMessageStackTrace()); - ProblemDetail problemDetail = createProblemDetail(errorBuilder, ProblemDetailsConstant.SYSTEM_ERROR_TITLE); + ProblemDetail problemDetail = problemDetailsCreation.createProblemDetail(errorBuilder, + ProblemDetailsConstant.SYSTEM_ERROR_TITLE, + HttpStatus.INTERNAL_SERVER_ERROR); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .contentType(MediaType.APPLICATION_JSON) .body(problemDetail); } /** - * 上記のいずれにも当てはまらない例外をステータースコード500で返却する(本番環境、テスト環境用)。 + * 上記のいずれにも当てはまらない例外をステータースコード500で返却する。 * * @param e その他の例外 * @param req リクエスト @@ -73,19 +76,13 @@ public ResponseEntity handleException(SystemException e, HttpServ */ @ExceptionHandler(Exception.class) public ResponseEntity handleException(Exception e, HttpServletRequest req) { - ErrorMessageBuilder errorBuilder = new ErrorMessageBuilder(e, ExceptionIdConstant.E_SHARE0000, null, null); + ErrorMessageBuilder errorBuilder = new ErrorMessageBuilder(e, CommonExceptionIdConstant.E_SYSTEM, null, null); apLog.error(errorBuilder.createLogMessageStackTrace()); - ProblemDetail problemDetail = createProblemDetail(errorBuilder, ProblemDetailsConstant.SYSTEM_ERROR_TITLE); + ProblemDetail problemDetail = problemDetailsCreation.createProblemDetail(errorBuilder, + ProblemDetailsConstant.SYSTEM_ERROR_TITLE, + HttpStatus.INTERNAL_SERVER_ERROR); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .contentType(MediaType.APPLICATION_JSON) .body(problemDetail); } - - private ProblemDetail createProblemDetail(ErrorMessageBuilder errorBuilder, String title) { - Map errorProperty = Map.of(errorBuilder.getExceptionId(), errorBuilder.createFrontErrorMessage()); - ProblemDetail problemDetail = ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR); - problemDetail.setTitle(title); - problemDetail.setProperty(ProblemDetailsConstant.ERROR_KEY, errorProperty); - return problemDetail; - } } diff --git a/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/advice/LocalExceptionHandlerControllerAdvice.java b/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/advice/LocalExceptionHandlerControllerAdvice.java deleted file mode 100644 index 4b65b4431..000000000 --- a/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/advice/LocalExceptionHandlerControllerAdvice.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.dressca.web.controller.advice; - -import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Profile; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ProblemDetail; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; -import com.dressca.systemcommon.constant.ExceptionIdConstant; -import com.dressca.systemcommon.constant.SystemPropertyConstants; -import com.dressca.systemcommon.exception.LogicException; -import com.dressca.systemcommon.exception.SystemException; -import com.dressca.web.constant.ProblemDetailsConstant; -import com.dressca.web.log.ErrorMessageBuilder; -import jakarta.servlet.http.HttpServletRequest; - -/** - * サーバーエラーのハンドリングを行うクラスです(開発環境用)。 - */ -@ControllerAdvice -@Profile("local") -public class LocalExceptionHandlerControllerAdvice extends ResponseEntityExceptionHandler { - - private static final Logger apLog = LoggerFactory.getLogger(SystemPropertyConstants.APPLICATION_LOG_LOGGER); - - /** - * その他の業務エラーをステータースコード500で返却する(開発環境用)。 - * - * @param e 業務例外 - * @param req リクエスト - * @return ステータースコード500のレスポンス - */ - @ExceptionHandler(LogicException.class) - public ResponseEntity localHandleLogicException(LogicException e, HttpServletRequest req) { - ErrorMessageBuilder errorBuilder = new ErrorMessageBuilder(e, e.getExceptionId(), e.getLogMessageValue(), - e.getFrontMessageValue()); - apLog.error(errorBuilder.createLogMessageStackTrace()); - ProblemDetail problemDetail = createProblemDetail(errorBuilder, ProblemDetailsConstant.LOGIC_ERROR_TITLE); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .contentType(MediaType.APPLICATION_JSON) - .body(problemDetail); - } - - /** - * その他のシステムエラーをステータースコード500で返却する(開発環境用)。 - * - * @param e その他の例外 - * @param req リクエスト - * @return ステータースコード500のレスポンス - */ - @ExceptionHandler(SystemException.class) - public ResponseEntity localHandleException(SystemException e, HttpServletRequest req) { - ErrorMessageBuilder errorBuilder = new ErrorMessageBuilder(e, e.getExceptionId(), e.getLogMessageValue(), - e.getFrontMessageValue()); - apLog.error(errorBuilder.createLogMessageStackTrace()); - ProblemDetail problemDetail = createProblemDetail(errorBuilder, ProblemDetailsConstant.SYSTEM_ERROR_TITLE); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .contentType(MediaType.APPLICATION_JSON) - .body(problemDetail); - } - - /** - * 上記のいずれにも当てはまらない例外をステータースコード500で返却する(開発環境用)。 - * - * @param e その他の例外 - * @param req リクエスト - * @return ステータースコード500のレスポンス - */ - @ExceptionHandler(Exception.class) - public ResponseEntity localHandleException(Exception e, HttpServletRequest req) { - ErrorMessageBuilder errorBuilder = new ErrorMessageBuilder(e, ExceptionIdConstant.E_SHARE0000, null, null); - apLog.error(errorBuilder.createLogMessageStackTrace()); - ProblemDetail problemDetail = createProblemDetail(errorBuilder, ProblemDetailsConstant.SYSTEM_ERROR_TITLE); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .contentType(MediaType.APPLICATION_JSON) - .body(problemDetail); - } - - private ProblemDetail createProblemDetail(ErrorMessageBuilder errorBuilder, String title) { - Map errorProperty = Map.of(errorBuilder.getExceptionId(), errorBuilder.createFrontErrorMessage()); - ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.INTERNAL_SERVER_ERROR, - errorBuilder.createLogMessageStackTrace()); - problemDetail.setTitle(title); - problemDetail.setProperty(ProblemDetailsConstant.ERROR_KEY, errorProperty); - return problemDetail; - } -} diff --git a/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/advice/ProblemDetailsCreation.java b/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/advice/ProblemDetailsCreation.java new file mode 100644 index 000000000..98c5a3f4d --- /dev/null +++ b/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/controller/advice/ProblemDetailsCreation.java @@ -0,0 +1,60 @@ +package com.dressca.web.controller.advice; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.http.HttpStatus; +import org.springframework.http.ProblemDetail; +import org.springframework.stereotype.Component; +import com.dressca.web.constant.ProblemDetailsConstant; +import com.dressca.web.log.ErrorMessageBuilder; + +/** + * エラーレスポンスに含める ProblemDetails を作成するクラスです。 + */ +@Component +public class ProblemDetailsCreation { + + @Autowired + private Environment env; + + /** + * その他のシステムエラーをステータースコード500で返却する。 + * + * @param errorBuilder 例外ビルダー + * @param title タイトル + * @param status ステータスコード + * @return エラーレスポンスに格納する ProblemDetails + */ + public ProblemDetail createProblemDetail(ErrorMessageBuilder errorBuilder, String title, HttpStatus status) { + + ProblemDetail problemDetail = ProblemDetail.forStatus(status); + + problemDetail.setTitle(title); + + String[] activeProfiles = env.getActiveProfiles(); + if (activeProfiles.length == 0) { + activeProfiles = env.getDefaultProfiles(); + } + + // local 環境においては detail を含める + if (Arrays.stream(activeProfiles).filter(profile -> Objects.equals(profile, "local")) + .findFirst().isPresent()) { + problemDetail.setDetail(errorBuilder.createLogMessageStackTrace()); + } + + Map errorProperty = new LinkedHashMap() { + { + put(ProblemDetailsConstant.EXCEPTION_ID, errorBuilder.getExceptionId()); + put(ProblemDetailsConstant.EXCEPTION_VALUES, errorBuilder.getFrontMessageValue()); + } + }; + + problemDetail.setProperties(errorProperty); + + return problemDetail; + } +} diff --git a/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/log/ErrorMessageBuilder.java b/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/log/ErrorMessageBuilder.java index 2b4cd563b..03d8849f0 100644 --- a/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/log/ErrorMessageBuilder.java +++ b/samples/web-csr/dressca-backend/web/src/main/java/com/dressca/web/log/ErrorMessageBuilder.java @@ -10,15 +10,12 @@ import lombok.Getter; /** - * ログやレスポンスでエラーメッセージを作成するためのクラスです。 + * ログでエラーメッセージを作成するためのクラスです。 */ @Getter @AllArgsConstructor public class ErrorMessageBuilder { - private static final String EXCEPTION_MESSAGE_SUFFIX_LOG = "log"; - private static final String EXCEPTION_MESSAGE_SUFFIX_FRONT = "front"; - private static final String PROPERTY_DELIMITER = "."; private static final MessageSource messageSource = (MessageSource) ApplicationContextWrapper .getBean(MessageSource.class); @@ -34,22 +31,11 @@ public class ErrorMessageBuilder { */ public String createLogMessageStackTrace() { StringBuilder builder = new StringBuilder(); - String code = String.join(PROPERTY_DELIMITER, exceptionId, EXCEPTION_MESSAGE_SUFFIX_LOG); - String exceptionMessage = messageSource.getMessage(code, logMessageValue, Locale.getDefault()); + String exceptionMessage = messageSource.getMessage(exceptionId, logMessageValue, Locale.getDefault()); builder.append(exceptionId).append(" ").append(exceptionMessage).append(SystemPropertyConstants.LINE_SEPARATOR); StringWriter writer = new StringWriter(); ex.printStackTrace(new PrintWriter(writer)); builder.append(writer.getBuffer().toString()); return builder.toString(); } - - /** - * ProblemDetailsのerror情報に格納するメッセージを作成します。 - * - * @return エラーメッセージ - */ - public String createFrontErrorMessage() { - String code = String.join(PROPERTY_DELIMITER, exceptionId, EXCEPTION_MESSAGE_SUFFIX_FRONT); - return messageSource.getMessage(code, frontMessageValue, Locale.getDefault()); - } } diff --git a/samples/web-csr/dressca-backend/web/src/main/resources/application-common.properties b/samples/web-csr/dressca-backend/web/src/main/resources/application-common.properties index bab1946c6..4b7014a41 100644 --- a/samples/web-csr/dressca-backend/web/src/main/resources/application-common.properties +++ b/samples/web-csr/dressca-backend/web/src/main/resources/application-common.properties @@ -3,6 +3,10 @@ springdoc.api-docs.path=/api-docs springdoc.swagger-ui.path=/swagger-ui.html springdoc.show-actuator=true +# messages.properties +spring.messages.basename=applicationcore.messages,systemcommon.messages +spring.messages.encoding=UTF-8 + # spring boot actuatorの設定(ステータスの変更,表示情報) # ヘルスチェックAPIのURLを/apiから始まるように変更する management.endpoints.web.base-path=/api diff --git a/samples/web-csr/dressca-backend/web/src/main/resources/application.properties b/samples/web-csr/dressca-backend/web/src/main/resources/application.properties index 41fa11b1e..7c8672e4a 100644 --- a/samples/web-csr/dressca-backend/web/src/main/resources/application.properties +++ b/samples/web-csr/dressca-backend/web/src/main/resources/application.properties @@ -3,7 +3,7 @@ spring.profiles.group.local=common,dev spring.profiles.group.production=common,prd spring.profiles.group.test=common,ut # 環境情報未指定の場合に使用するプロファイル(環境情報を指定する場合、起動コマンドに「-Dspring.profiles.active=<プロファイル名>'」を追加する) -spring.profiles.default=local +spring.profiles.default=production # Open API 仕様書を自動生成する際keyをアルファベット順でソートする springdoc.writer-with-order-by-keys=true \ No newline at end of file diff --git a/samples/web-csr/dressca-backend/web/src/main/resources/log4j2.xml b/samples/web-csr/dressca-backend/web/src/main/resources/log4j2.xml new file mode 100644 index 000000000..3de3d7921 --- /dev/null +++ b/samples/web-csr/dressca-backend/web/src/main/resources/log4j2.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/web-csr/dressca-backend/web/src/test/java/com/dressca/web/controller/advice/ExceptionHandlerControllerAdviceTest.java b/samples/web-csr/dressca-backend/web/src/test/java/com/dressca/web/controller/advice/ExceptionHandlerControllerAdviceTest.java index 4105385ca..3cb67f3e0 100644 --- a/samples/web-csr/dressca-backend/web/src/test/java/com/dressca/web/controller/advice/ExceptionHandlerControllerAdviceTest.java +++ b/samples/web-csr/dressca-backend/web/src/test/java/com/dressca/web/controller/advice/ExceptionHandlerControllerAdviceTest.java @@ -9,8 +9,9 @@ import static org.mockito.Mockito.times; import com.dressca.web.controller.AssetsController; -import com.dressca.systemcommon.constant.ExceptionIdConstant; +import com.dressca.systemcommon.constant.CommonExceptionIdConstant; import com.dressca.systemcommon.constant.SystemPropertyConstants; +import com.dressca.systemcommon.exception.LogicException; import com.dressca.systemcommon.exception.SystemException; import com.dressca.systemcommon.util.ApplicationContextWrapper; import com.dressca.applicationcore.assets.AssetNotFoundException; @@ -49,9 +50,6 @@ @ActiveProfiles("production") public class ExceptionHandlerControllerAdviceTest { - private static final String EXCEPTION_MESSAGE_SUFFIX_LOG = "log"; - private static final String EXCEPTION_MESSAGE_SUFFIX_FRONT = "front"; - private static final String PROPERTY_DELIMITER = "."; private static final String MOCK_APPENDER_NAME = "MockAppender"; @Autowired @@ -103,18 +101,19 @@ void testException_01() throws Exception { // テスト用の入力データ String assetCode = "b52dc7f712d94ca5812dd995bf926c04"; // 期待値の設定 - String exceptionId = ExceptionIdConstant.E_ASSET0001; - String[] frontMessageValue = { assetCode }; - String[] logMessageValue = { assetCode }; + String exceptionId = CommonExceptionIdConstant.E_BUSINESS; + String[] frontMessageValue = null; + String[] logMessageValue = null; // モックの戻り値設定 Mockito.when(assetsController.get(anyString())) - .thenThrow(new AssetNotFoundException(assetCode)); + .thenThrow(new LogicException(new AssetNotFoundException(assetCode), exceptionId, + frontMessageValue, logMessageValue)); // APIの呼び出しとエラー時のレスポンスであることの確認 this.mockMvc.perform(get("/api/assets/" + assetCode)) .andExpect(status().isInternalServerError()) .andExpect(content().json("{\"title\":\"" + ProblemDetailsConstant.LOGIC_ERROR_TITLE + "\"}")) - .andExpect(jsonPath("$.error." + exceptionId) - .value(createFrontErrorMessage(exceptionId, frontMessageValue))) + .andExpect(jsonPath("$.exceptionId").value(exceptionId)) + .andExpect(jsonPath("$.exceptionValues").value(frontMessageValue)) .andExpect(jsonPath("$.detail").doesNotExist()); // アプリケーションログのメッセージの確認 Mockito.verify(mockAppender, times(1)).append(logCaptor.capture()); @@ -129,19 +128,20 @@ void testException_02() throws Exception { // テスト用の入力データ String assetCode = "b52dc7f712d94ca5812dd995bf926c04"; // 期待値の設定 - String exceptionId = ExceptionIdConstant.E_SHARE0000; + String exceptionId = CommonExceptionIdConstant.E_SYSTEM; String[] frontMessageValue = null; String[] logMessageValue = null; // モックの戻り値設定 Mockito.when(assetsController.get(anyString())) - .thenThrow(new SystemException(new AssetNotFoundException(assetCode), exceptionId, frontMessageValue, + .thenThrow(new SystemException(new AssetNotFoundException(assetCode), + exceptionId, frontMessageValue, logMessageValue)); // APIの呼び出しとエラー時のレスポンスであることの確認 this.mockMvc.perform(get("/api/assets/" + assetCode)) .andExpect(status().isInternalServerError()) .andExpect(content().json("{\"title\":\"" + ProblemDetailsConstant.SYSTEM_ERROR_TITLE + "\"}")) - .andExpect(jsonPath("$.error." + exceptionId) - .value(createFrontErrorMessage(exceptionId, frontMessageValue))) + .andExpect(jsonPath("$.exceptionId").value(exceptionId)) + .andExpect(jsonPath("$.exceptionValues").value(frontMessageValue)) .andExpect(jsonPath("$.detail").doesNotExist()); // アプリケーションログのメッセージの確認 Mockito.verify(mockAppender, times(1)).append(logCaptor.capture()); @@ -156,7 +156,7 @@ void testException_03() throws Exception { // テスト用の入力データ String assetCode = "b52dc7f712d94ca5812dd995bf926c04"; // 期待値の設定 - String exceptionId = ExceptionIdConstant.E_SHARE0000; + String exceptionId = CommonExceptionIdConstant.E_SYSTEM; String[] frontMessageValue = null; String[] logMessageValue = null; // モックの戻り値設定 @@ -166,8 +166,8 @@ void testException_03() throws Exception { this.mockMvc.perform(get("/api/assets/" + assetCode)) .andExpect(status().isInternalServerError()) .andExpect(content().json("{\"title\":\"" + ProblemDetailsConstant.SYSTEM_ERROR_TITLE + "\"}")) - .andExpect(jsonPath("$.error." + exceptionId) - .value(createFrontErrorMessage(exceptionId, frontMessageValue))) + .andExpect(jsonPath("$.exceptionId").value(exceptionId)) + .andExpect(jsonPath("$.exceptionValues").value(frontMessageValue)) .andExpect(jsonPath("$.detail").doesNotExist()); // アプリケーションログのメッセージの確認 Mockito.verify(mockAppender, times(1)).append(logCaptor.capture()); @@ -179,15 +179,7 @@ void testException_03() throws Exception { // エラー時のアプリケーションログ出力メッセージの先頭行を返す(2行目以降はエラーのスタックトレースのため可変) private String createLogMessage(String exceptionId, String[] logMessageValue) { MessageSource messageSource = (MessageSource) ApplicationContextWrapper.getBean(MessageSource.class); - String code = String.join(PROPERTY_DELIMITER, exceptionId, EXCEPTION_MESSAGE_SUFFIX_LOG); - String exceptionMessage = messageSource.getMessage(code, logMessageValue, Locale.getDefault()); + String exceptionMessage = messageSource.getMessage(exceptionId, logMessageValue, Locale.getDefault()); return exceptionId + " " + exceptionMessage + SystemPropertyConstants.LINE_SEPARATOR; } - - // エラー時のフロントに出力するメッセージを返す - private String createFrontErrorMessage(String exceptionId, String[] frontMessageValue) { - String code = String.join(PROPERTY_DELIMITER, exceptionId, EXCEPTION_MESSAGE_SUFFIX_FRONT); - MessageSource messageSource = (MessageSource) ApplicationContextWrapper.getBean(MessageSource.class); - return messageSource.getMessage(code, frontMessageValue, Locale.getDefault()); - } } diff --git a/samples/web-csr/dressca-backend/web/src/test/java/com/dressca/web/controller/advice/LocalExceptionHandlerControllerAdviceTest.java b/samples/web-csr/dressca-backend/web/src/test/java/com/dressca/web/controller/advice/LocalExceptionHandlerControllerAdviceTest.java index 2a181d39d..feab9e3fb 100644 --- a/samples/web-csr/dressca-backend/web/src/test/java/com/dressca/web/controller/advice/LocalExceptionHandlerControllerAdviceTest.java +++ b/samples/web-csr/dressca-backend/web/src/test/java/com/dressca/web/controller/advice/LocalExceptionHandlerControllerAdviceTest.java @@ -9,8 +9,9 @@ import static org.mockito.Mockito.times; import com.dressca.web.controller.AssetsController; -import com.dressca.systemcommon.constant.ExceptionIdConstant; +import com.dressca.systemcommon.constant.CommonExceptionIdConstant; import com.dressca.systemcommon.constant.SystemPropertyConstants; +import com.dressca.systemcommon.exception.LogicException; import com.dressca.systemcommon.exception.SystemException; import com.dressca.systemcommon.util.ApplicationContextWrapper; import com.dressca.applicationcore.assets.AssetNotFoundException; @@ -49,9 +50,6 @@ @ActiveProfiles("local") public class LocalExceptionHandlerControllerAdviceTest { - private static final String EXCEPTION_MESSAGE_SUFFIX_LOG = "log"; - private static final String EXCEPTION_MESSAGE_SUFFIX_FRONT = "front"; - private static final String PROPERTY_DELIMITER = "."; private static final String MOCK_APPENDER_NAME = "MockAppender"; @Autowired @@ -103,19 +101,21 @@ void testException_01() throws Exception { // テスト用の入力データ String assetCode = "b52dc7f712d94ca5812dd995bf926c04"; // 期待値の設定 - String exceptionId = ExceptionIdConstant.E_ASSET0001; - String[] frontMessageValue = { assetCode }; - String[] logMessageValue = { assetCode }; + String exceptionId = CommonExceptionIdConstant.E_BUSINESS; + String[] frontMessageValue = null; + String[] logMessageValue = null; // モックの戻り値設定 Mockito.when(assetsController.get(anyString())) - .thenThrow(new AssetNotFoundException(assetCode)); + .thenThrow(new LogicException(new AssetNotFoundException(assetCode), exceptionId, + frontMessageValue, logMessageValue)); // APIの呼び出しとエラー時のレスポンスであることの確認 this.mockMvc.perform(get("/api/assets/" + assetCode)) .andExpect(status().isInternalServerError()) .andExpect(content().json("{\"title\":\"" + ProblemDetailsConstant.LOGIC_ERROR_TITLE + "\"}")) - .andExpect(jsonPath("$.error." + exceptionId) - .value(createFrontErrorMessage(exceptionId, frontMessageValue))) + .andExpect(jsonPath("$.exceptionId").value(exceptionId)) + .andExpect(jsonPath("$.exceptionValues").value(frontMessageValue)) .andExpect(jsonPath("$.detail").exists()); + // アプリケーションログのメッセージの確認 Mockito.verify(mockAppender, times(1)).append(logCaptor.capture()); assertThat(logCaptor.getValue().getLevel()).isEqualTo(Level.ERROR); assertThat(logCaptor.getValue().getMessage().getFormattedMessage()) @@ -128,19 +128,19 @@ void testException_02() throws Exception { // テスト用の入力データ String assetCode = "b52dc7f712d94ca5812dd995bf926c04"; // 期待値の設定 - String exceptionId = ExceptionIdConstant.E_SHARE0000; + String exceptionId = CommonExceptionIdConstant.E_SYSTEM; String[] frontMessageValue = null; String[] logMessageValue = null; // モックの戻り値設定 Mockito.when(assetsController.get(anyString())) - .thenThrow(new SystemException(new AssetNotFoundException(assetCode), exceptionId, frontMessageValue, + .thenThrow(new SystemException(null, exceptionId, frontMessageValue, logMessageValue)); // APIの呼び出しとエラー時のレスポンスであることの確認 this.mockMvc.perform(get("/api/assets/" + assetCode)) .andExpect(status().isInternalServerError()) .andExpect(content().json("{\"title\":\"" + ProblemDetailsConstant.SYSTEM_ERROR_TITLE + "\"}")) - .andExpect(jsonPath("$.error." + exceptionId) - .value(createFrontErrorMessage(exceptionId, frontMessageValue))) + .andExpect(jsonPath("$.exceptionId").value(exceptionId)) + .andExpect(jsonPath("$.exceptionValues").value(frontMessageValue)) .andExpect(jsonPath("$.detail").exists()); // アプリケーションログのメッセージの確認 Mockito.verify(mockAppender, times(1)).append(logCaptor.capture()); @@ -155,7 +155,7 @@ void testException_03() throws Exception { // テスト用の入力データ String assetCode = "b52dc7f712d94ca5812dd995bf926c04"; // 期待値の設定 - String exceptionId = ExceptionIdConstant.E_SHARE0000; + String exceptionId = CommonExceptionIdConstant.E_SYSTEM; String[] frontMessageValue = null; String[] logMessageValue = null; // モックの戻り値設定 @@ -165,8 +165,8 @@ void testException_03() throws Exception { this.mockMvc.perform(get("/api/assets/" + assetCode)) .andExpect(status().isInternalServerError()) .andExpect(content().json("{\"title\":\"" + ProblemDetailsConstant.SYSTEM_ERROR_TITLE + "\"}")) - .andExpect(jsonPath("$.error." + exceptionId) - .value(createFrontErrorMessage(exceptionId, frontMessageValue))) + .andExpect(jsonPath("$.exceptionId").value(exceptionId)) + .andExpect(jsonPath("$.exceptionValues").value(frontMessageValue)) .andExpect(jsonPath("$.detail").exists()); // アプリケーションログのメッセージの確認 Mockito.verify(mockAppender, times(1)).append(logCaptor.capture()); @@ -178,15 +178,7 @@ void testException_03() throws Exception { // エラー時のアプリケーションログ出力メッセージの先頭行を返す(2行目以降はエラーのスタックトレースのため可変) private String createLogMessage(String exceptionId, String[] logMessageValue) { MessageSource messageSource = (MessageSource) ApplicationContextWrapper.getBean(MessageSource.class); - String code = String.join(PROPERTY_DELIMITER, exceptionId, EXCEPTION_MESSAGE_SUFFIX_LOG); - String exceptionMessage = messageSource.getMessage(code, logMessageValue, Locale.getDefault()); + String exceptionMessage = messageSource.getMessage(exceptionId, logMessageValue, Locale.getDefault()); return exceptionId + " " + exceptionMessage + SystemPropertyConstants.LINE_SEPARATOR; } - - // エラー時のフロントに出力するメッセージを返す - private String createFrontErrorMessage(String exceptionId, String[] frontMessageValue) { - String code = String.join(PROPERTY_DELIMITER, exceptionId, EXCEPTION_MESSAGE_SUFFIX_FRONT); - MessageSource messageSource = (MessageSource) ApplicationContextWrapper.getBean(MessageSource.class); - return messageSource.getMessage(code, frontMessageValue, Locale.getDefault()); - } } diff --git a/samples/web-csr/dressca-frontend/consumer/package.json b/samples/web-csr/dressca-frontend/consumer/package.json index f3922bf61..08158ddd1 100644 --- a/samples/web-csr/dressca-frontend/consumer/package.json +++ b/samples/web-csr/dressca-frontend/consumer/package.json @@ -26,6 +26,7 @@ "pinia": "^2.2.5", "vee-validate": "^4.14.4", "vue": "^3.5.12", + "vue-i18n": "^10.0.4", "vue-router": "^4.4.5", "yup": "^1.4.0" }, diff --git a/samples/web-csr/dressca-frontend/consumer/src/App.vue b/samples/web-csr/dressca-frontend/consumer/src/App.vue index bdb7cfa24..c98915cab 100644 --- a/samples/web-csr/dressca-frontend/consumer/src/App.vue +++ b/samples/web-csr/dressca-frontend/consumer/src/App.vue @@ -21,7 +21,7 @@ const isAuthenticated = () => { >
- Dressca + Dressca
diff --git a/samples/web-csr/dressca-frontend/consumer/src/api-client/index.ts b/samples/web-csr/dressca-frontend/consumer/src/api-client/index.ts index 1a180ddac..d588a7a87 100644 --- a/samples/web-csr/dressca-frontend/consumer/src/api-client/index.ts +++ b/samples/web-csr/dressca-frontend/consumer/src/api-client/index.ts @@ -26,15 +26,13 @@ axiosInstance.interceptors.response.use( (error) => { if (axios.isAxiosError(error)) { if (!error.response) { - return Promise.reject(new NetworkError('Network Error', error)); + return Promise.reject(new NetworkError(error.message, error)); } if (error.response.status === 500) { - return Promise.reject(new ServerError('Server Error', error)); + return Promise.reject(new ServerError(error.message, error)); } if (error.response.status === 401) { - return Promise.reject( - new UnauthorizedError('Unauthorized Error', error), - ); + return Promise.reject(new UnauthorizedError(error.message, error)); } return Promise.reject(new HttpError(error.message, error)); diff --git a/samples/web-csr/dressca-frontend/consumer/src/components/basket/BasketItem.vue b/samples/web-csr/dressca-frontend/consumer/src/components/basket/BasketItem.vue index b5304b702..61af33c1f 100644 --- a/samples/web-csr/dressca-frontend/consumer/src/components/basket/BasketItem.vue +++ b/samples/web-csr/dressca-frontend/consumer/src/components/basket/BasketItem.vue @@ -93,7 +93,8 @@ const remove = () => {
- 小計: {{ toCurrencyJPY(item.subTotal) }} + 小計: + {{ toCurrencyJPY(item.subTotal) }}
diff --git a/samples/web-csr/dressca-frontend/consumer/src/components/common/NotificationToast.vue b/samples/web-csr/dressca-frontend/consumer/src/components/common/NotificationToast.vue index 0e54845fa..7d2d07e70 100644 --- a/samples/web-csr/dressca-frontend/consumer/src/components/common/NotificationToast.vue +++ b/samples/web-csr/dressca-frontend/consumer/src/components/common/NotificationToast.vue @@ -1,8 +1,11 @@ + diff --git a/samples/web-csr/dressca-frontend/consumer/src/views/ordering/CheckoutView.vue b/samples/web-csr/dressca-frontend/consumer/src/views/ordering/CheckoutView.vue index 48575743a..7bf658ca3 100644 --- a/samples/web-csr/dressca-frontend/consumer/src/views/ordering/CheckoutView.vue +++ b/samples/web-csr/dressca-frontend/consumer/src/views/ordering/CheckoutView.vue @@ -5,12 +5,14 @@ import { useUserStore } from '@/stores/user/user'; import { postOrder } from '@/services/ordering/ordering-service'; import { fetchBasket } from '@/services/basket/basket-service'; import { showToast } from '@/services/notification/notificationService'; - import { useRouter } from 'vue-router'; import { currencyHelper } from '@/shared/helpers/currencyHelper'; import { assetHelper } from '@/shared/helpers/assetHelper'; import { storeToRefs } from 'pinia'; import { useCustomErrorHandler } from '@/shared/error-handler/use-custom-error-handler'; +import { i18n } from '@/locales/i18n'; +import { errorMessageFormat } from '@/shared/error-handler/error-message-format'; +import { HttpError } from '@/shared/error-handler/custom-error'; const userStore = useUserStore(); const basketStore = useBasketStore(); @@ -21,6 +23,7 @@ const router = useRouter(); const customErrorHandler = useCustomErrorHandler(); const { toCurrencyJPY } = currencyHelper(); const { getFirstAssetUrl } = assetHelper(); +const { t } = i18n.global; const checkout = async () => { try { @@ -33,13 +36,32 @@ const checkout = async () => { ); router.push({ name: 'ordering/done', params: { orderId } }); } catch (error) { - customErrorHandler.handle(error, () => { - showToast('注文に失敗しました。'); - router.push({ name: 'error' }); - }); + customErrorHandler.handle( + error, + () => { + router.push({ name: 'error' }); + }, + (httpError: HttpError) => { + if (!httpError.response) { + showToast(t('failedToOrderItems')); + } else { + const message = errorMessageFormat( + httpError.response.exceptionId, + httpError.response.exceptionValues, + ); + showToast( + message, + httpError.response.exceptionId, + httpError.response.title, + httpError.response.detail, + httpError.response.status, + 100000, + ); + } + }, + ); } }; - onMounted(async () => { await fetchBasket(); if (getBasket.value.basketItems?.length === 0) { @@ -51,7 +73,7 @@ onMounted(async () => {