# 機能設計書 3-メニュー管理

## 概要

本ドキュメントは、RuoYiシステムにおけるメニュー管理機能の設計仕様を記載する。メニュー管理機能は、システムのナビゲーションメニューとアクセス権限を管理する機能である。

### 本機能の処理概要

メニュー管理機能は、システム管理者がメニュー項目（ディレクトリ・メニュー・ボタン）の作成・変更・削除・照会を行うための機能を提供する。メニューは階層構造を持ち、ロールに紐付けることで画面やボタンレベルのアクセス制御を実現する。

**業務上の目的・背景**：企業システムにおいて、機能追加や組織変更に応じてナビゲーションメニューを柔軟に変更できることは運用上重要である。本機能により、管理者はプログラム改修なしにメニュー構成を変更し、ロールベースの権限制御を設定できる。

**機能の利用シーン**：
- 新機能追加に伴うメニュー項目の追加時
- 機能廃止に伴うメニューの削除・非表示化時
- メニュー構成の再編成（順序変更、階層変更）時
- ボタンレベルの権限設定時
- メニューアイコンの変更時

**主要な処理内容**：
1. メニュー一覧のツリー表示・検索
2. メニューの新規登録（ディレクトリ/メニュー/ボタン）
3. メニュー情報の編集・更新
4. メニューの削除
5. メニュー表示順序の更新
6. メニューアイコンの選択
7. ロール用メニューツリーの取得

**関連システム・外部連携**：本機能は単独で動作し、外部システムとの直接連携はない。ロール管理機能との連携により、メニュー権限の設定に使用される。

**権限による制御**：
- `system:menu:view` - メニュー一覧画面の表示権限
- `system:menu:list` - メニュー一覧の取得権限
- `system:menu:add` - メニュー新規登録権限
- `system:menu:edit` - メニュー編集権限
- `system:menu:remove` - メニュー削除権限

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 25 | メニュー管理一覧 | 主画面 | メニュー一覧のツリー表示、検索処理 |
| 26 | メニュー新規登録 | 主画面 | 新規メニュー情報入力と登録保存処理 |
| 27 | メニュー編集 | 主画面 | メニュー情報の更新保存処理 |
| 28 | メニューツリー選択 | 補助画面 | 親メニュー選択用ツリー表示処理 |
| 29 | アイコン選択 | 補助画面 | Font Awesomeアイコン一覧表示と選択処理 |

## 機能種別

CRUD操作 / ツリー構造管理 / バリデーション

## 入力仕様

### 入力パラメータ

#### メニュー検索条件

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| menuName | String | No | メニュー名（部分一致） | 最大50文字 |
| visible | String | No | 表示状態 | 0:表示, 1:非表示 |

#### メニュー登録・更新

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| menuId | Long | 更新時必須 | メニューID | 数値 |
| parentId | Long | Yes | 親メニューID | 数値（0はルート） |
| menuName | String | Yes | メニュー名 | 必須、最大50文字、一意性チェック |
| orderNum | String | Yes | 表示順序 | 必須 |
| url | String | No | メニューURL | 最大200文字 |
| target | String | No | 開き方 | menuItem/menuBlank |
| menuType | String | Yes | メニュー種別 | 必須、M:ディレクトリ, C:メニュー, F:ボタン |
| visible | String | No | 表示状態 | 0:表示, 1:非表示 |
| isRefresh | String | No | リフレッシュ | 0:する, 1:しない |
| perms | String | No | 権限文字列 | 最大100文字 |
| icon | String | No | アイコン | - |

### 入力データソース

- 画面入力：管理者による直接入力
- アイコン選択：Font Awesomeアイコン一覧からの選択

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| menuId | Long | メニューID |
| menuName | String | メニュー名 |
| parentId | Long | 親メニューID |
| parentName | String | 親メニュー名 |
| orderNum | String | 表示順序 |
| url | String | メニューURL |
| target | String | 開き方 |
| menuType | String | メニュー種別 |
| visible | String | 表示状態 |
| isRefresh | String | リフレッシュ |
| perms | String | 権限文字列 |
| icon | String | アイコン |
| children | List&lt;SysMenu&gt; | 子メニュー一覧 |

### 出力先

- 画面表示：メニュー一覧（ツリー形式）

## 処理フロー

### 処理シーケンス

```
1. メニュー一覧取得
   └─ 検索条件を受け取り、ユーザーIDに基づいてメニュー一覧を取得
   └─ ツリー構造に変換して返却

2. メニュー新規登録
   └─ 入力バリデーション
   └─ 同一親メニュー配下でのメニュー名一意性チェック
   └─ メニュー情報登録
   └─ 認可キャッシュクリア

3. メニュー更新
   └─ 入力バリデーション
   └─ 一意性チェック
   └─ メニュー情報更新
   └─ 認可キャッシュクリア

4. メニュー削除
   └─ 子メニュー存在チェック（存在する場合は削除不可）
   └─ ロール割当チェック（割当済みの場合は削除不可）
   └─ メニュー削除
   └─ 認可キャッシュクリア

5. 表示順序更新
   └─ 複数メニューの表示順序を一括更新

6. ロール用メニューツリー取得
   └─ ユーザーIDとロールIDに基づいてメニューツリーを取得
   └─ ロールに割当済みのメニューにはチェック状態を設定
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{操作種別}
    B -->|一覧取得| C[検索条件取得]
    C --> D[ユーザー権限に基づくメニュー取得]
    D --> E[ツリー構造に変換]
    E --> Z[終了]

    B -->|新規登録| F[入力バリデーション]
    F --> G{バリデーションOK?}
    G -->|No| H[エラー返却]
    H --> Z
    G -->|Yes| I[一意性チェック]
    I --> J{一意?}
    J -->|No| H
    J -->|Yes| K[メニュー登録]
    K --> L[認可キャッシュクリア]
    L --> Z

    B -->|削除| M[子メニュー存在チェック]
    M --> N{子メニューあり?}
    N -->|Yes| H
    N -->|No| O[ロール割当チェック]
    O --> P{割当あり?}
    P -->|Yes| H
    P -->|No| Q[メニュー削除]
    Q --> R[認可キャッシュクリア]
    R --> Z
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 子メニュー存在時削除禁止 | 子メニューが存在するメニューは削除できない | メニュー削除時 |
| BR-002 | ロール割当時削除禁止 | ロールに割り当てられているメニューは削除できない | メニュー削除時 |
| BR-003 | メニュー名一意性 | 同一親メニュー配下でメニュー名は一意でなければならない | メニュー登録・更新時 |
| BR-004 | メニュー種別制約 | M:ディレクトリ, C:メニュー, F:ボタン のいずれか | メニュー登録・更新時 |
| BR-005 | 階層構造維持 | 親メニューIDは有効なメニューIDでなければならない | メニュー登録・更新時 |

### メニュー種別

| 値 | 説明 |
|---|------|
| M | ディレクトリ（目录）- 子メニューを持つフォルダ |
| C | メニュー（菜单）- 画面遷移を伴うメニュー項目 |
| F | ボタン（按钮）- 画面内の操作ボタン |

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| メニュー一覧取得 | sys_menu | SELECT | 条件に合致するメニュー一覧を取得 |
| メニュー登録 | sys_menu | INSERT | 新規メニュー情報を登録 |
| メニュー更新 | sys_menu | UPDATE | メニュー情報を更新 |
| メニュー削除 | sys_menu | DELETE | メニューを削除 |
| 子メニュー数確認 | sys_menu | SELECT COUNT | 子メニュー数を取得 |
| ロール割当確認 | sys_role_menu | SELECT COUNT | ロール割当数を取得 |

### テーブル別操作詳細

#### sys_menu

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | menu_id | 自動採番 | 主キー |
| INSERT | menu_name | 入力値 | メニュー名 |
| INSERT | parent_id | 入力値 | 親メニューID |
| INSERT | order_num | 入力値 | 表示順序 |
| INSERT | url | 入力値 | メニューURL |
| INSERT | target | 入力値 | 開き方 |
| INSERT | menu_type | 入力値 | メニュー種別 |
| INSERT | visible | 入力値 | 表示状態 |
| INSERT | is_refresh | 入力値 | リフレッシュ |
| INSERT | perms | 入力値 | 権限文字列 |
| INSERT | icon | 入力値 | アイコン |
| INSERT | create_by | ログインユーザー名 | 作成者 |
| INSERT | create_time | sysdate() | 作成日時 |
| DELETE | menu_id | 対象メニューID | 物理削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | バリデーションエラー | 必須項目未入力、形式不正 | エラーメッセージを画面表示 |
| - | 一意性エラー | メニュー名が重複 | 「〜已存在」メッセージを表示 |
| - | 子メニュー存在エラー | 子メニューが存在する状態で削除 | 「存在子菜单,不允许删除」を表示 |
| - | ロール割当エラー | ロールに割当済みの状態で削除 | 「菜单已分配,不允许删除」を表示 |

### リトライ仕様

特になし。エラー発生時は即座にエラーメッセージを返却する。

## トランザクション仕様

- メニュー登録：単一INSERT処理
- メニュー更新：単一UPDATE処理
- メニュー削除：単一DELETE処理
- 表示順序更新：複数UPDATE処理を1トランザクションで実行

## パフォーマンス要件

- メニュー一覧取得：ツリー構造のため全件取得

## セキュリティ考慮事項

1. **権限制御**：Apache Shiroによるメソッドレベルの権限チェック
2. **権限文字列管理**：permsフィールドで権限を定義し、Shiroと連携
3. **監査ログ**：@Logアノテーションによる操作ログ記録
4. **認可キャッシュ管理**：メニュー変更時に認可キャッシュをクリアし、即座に反映

## 備考

- メニュー削除は物理削除となる
- メニューのURL設定で外部リンク（http://〜）も指定可能
- アイコンはFont Awesomeのクラス名を指定

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | SysMenu.java | `ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java` | メニューエンティティ、階層構造（children）、メニュー種別を確認 |

**読解のコツ**: `children`フィールド（56行目）でツリー構造を表現。`menuType`（M/C/F）で種別を区別。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SysMenuController.java | `ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java` | 各エンドポイント、ツリーデータ取得を確認 |

**主要処理フロー**:
- **47-55行目**: メニュー一覧取得（ユーザーIDに基づく）
- **59-76行目**: メニュー削除（子メニュー・ロール割当チェック）
- **103-116行目**: 新規登録保存
- **132-145行目**: 更新保存
- **178-199行目**: ツリーデータ取得

#### Step 3: ビジネスロジック層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | SysMenuServiceImpl.java | `ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java` | ツリー構造構築ロジック、一意性チェックを確認 |

### プログラム呼び出し階層図

```
SysMenuController
    │
    ├─ list() [メニュー一覧取得]
    │      └─ ISysMenuService.selectMenuList()
    │
    ├─ addSave() [メニュー新規登録]
    │      ├─ ISysMenuService.checkMenuNameUnique()
    │      ├─ ISysMenuService.insertMenu()
    │      └─ AuthorizationUtils.clearAllCachedAuthorizationInfo()
    │
    ├─ remove() [メニュー削除]
    │      ├─ ISysMenuService.selectCountMenuByParentId()
    │      ├─ ISysMenuService.selectCountRoleMenuByMenuId()
    │      ├─ ISysMenuService.deleteMenuById()
    │      └─ AuthorizationUtils.clearAllCachedAuthorizationInfo()
    │
    └─ roleMenuTreeData() [ロール用メニューツリー取得]
           └─ ISysMenuService.roleMenuTreeData()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SysMenuController.java | `ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java` | Controller | リクエスト受付 |
| SysMenu.java | `ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java` | Entity | メニューエンティティ |
| SysMenuServiceImpl.java | `ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java` | Service | ビジネスロジック |
| SysMenuMapper.java | `ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java` | Mapper | データアクセス |
