# 機能設計書 4-部門管理

## 概要

本ドキュメントは、RuoYiシステムにおける部門管理機能の設計仕様を記載する。部門管理機能は、組織の部門階層を管理し、データ権限制御の基盤となる機能である。

### 本機能の処理概要

部門管理機能は、システム管理者が部門の作成・変更・削除・照会を行うための機能を提供する。部門は階層構造（ツリー構造）を持ち、ユーザーの所属部門として設定される。また、ロールのデータ権限設定と連携し、部門単位でのデータアクセス制御を実現する。

**業務上の目的・背景**：企業・組織において、部門構造を適切に管理することは、人事管理やデータアクセス制御の基盤となる。本機能により、管理者は組織改編に柔軟に対応し、部門階層を維持しながらユーザー管理やデータ権限を設定できる。

**機能の利用シーン**：
- 新部門の設立時
- 組織改編に伴う部門統廃合時
- 部門名称変更時
- 部門責任者の変更時
- データ権限設定のための部門ツリー選択時

**主要な処理内容**：
1. 部門一覧のツリー表示・検索
2. 部門の新規登録
3. 部門情報の編集・更新
4. 部門の削除
5. 部門ツリーの取得（選択用）
6. ロール用部門ツリーの取得（データ権限設定用）

**関連システム・外部連携**：本機能は単独で動作し、外部システムとの直接連携はない。ユーザー管理、ロール管理機能と連携する。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 30 | 部門管理一覧 | 主画面 | 部門一覧のツリー表示、検索処理 |
| 31 | 部門新規登録 | 主画面 | 新規部門情報入力と登録保存処理 |
| 32 | 部門編集 | 主画面 | 部門情報の更新保存処理 |
| 33 | 部門ツリー選択 | 主画面 | 親部門選択用ツリー表示処理 |
| 15 | 部門ツリー選択 | 補助画面 | 部門ツリー表示と選択処理 |

## 機能種別

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

## 入力仕様

### 入力パラメータ

#### 部門検索条件

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| deptName | String | No | 部門名（部分一致） | 最大30文字 |
| status | String | No | ステータス | 0:正常, 1:停用 |

#### 部門登録・更新

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| deptId | Long | 更新時必須 | 部門ID | 数値 |
| parentId | Long | Yes | 親部門ID | 数値（0はルート） |
| deptName | String | Yes | 部門名 | 必須、最大30文字、一意性チェック |
| orderNum | Integer | Yes | 表示順序 | 必須 |
| leader | String | No | 責任者 | - |
| phone | String | No | 電話番号 | 最大11文字 |
| email | String | No | メールアドレス | メール形式、最大50文字 |
| status | String | No | ステータス | 0:正常, 1:停用 |

### 入力データソース

- 画面入力：管理者による直接入力

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| deptId | Long | 部門ID |
| parentId | Long | 親部門ID |
| ancestors | String | 祖先ID列（カンマ区切り） |
| deptName | String | 部門名 |
| orderNum | Integer | 表示順序 |
| leader | String | 責任者 |
| phone | String | 電話番号 |
| email | String | メールアドレス |
| status | String | ステータス |
| parentName | String | 親部門名 |

### 出力先

- 画面表示：部門一覧（ツリー形式）

## 処理フロー

### 処理シーケンス

```
1. 部門一覧取得
   └─ 検索条件を受け取り、部門一覧を取得

2. 部門新規登録
   └─ 入力バリデーション
   └─ 同一親部門配下での部門名一意性チェック
   └─ 祖先ID列（ancestors）の生成
   └─ 部門情報登録

3. 部門更新
   └─ 入力バリデーション
   └─ データスコープチェック
   └─ 一意性チェック
   └─ 自己参照チェック（自分自身を親にできない）
   └─ 停用時の子部門チェック
   └─ 部門情報更新
   └─ 子部門の祖先ID列更新（親変更時）

4. 部門削除
   └─ 下位部門存在チェック（存在する場合は削除不可）
   └─ ユーザー所属チェック（所属ユーザーがいる場合は削除不可）
   └─ データスコープチェック
   └─ 部門削除
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{操作種別}
    B -->|一覧取得| C[検索条件取得]
    C --> D[部門一覧取得]
    D --> Z[終了]

    B -->|新規登録| E[入力バリデーション]
    E --> F{バリデーションOK?}
    F -->|No| G[エラー返却]
    G --> Z
    F -->|Yes| H[一意性チェック]
    H --> I{一意?}
    I -->|No| G
    I -->|Yes| J[祖先ID列生成]
    J --> K[部門登録]
    K --> Z

    B -->|更新| L[データスコープチェック]
    L --> M{権限OK?}
    M -->|No| G
    M -->|Yes| N[入力バリデーション]
    N --> O[自己参照チェック]
    O --> P{自己参照?}
    P -->|Yes| G
    P -->|No| Q[停用時子部門チェック]
    Q --> R[部門更新]
    R --> Z

    B -->|削除| S[下位部門チェック]
    S --> T{下位部門あり?}
    T -->|Yes| G
    T -->|No| U[ユーザー所属チェック]
    U --> V{所属ユーザーあり?}
    V -->|Yes| G
    V -->|No| W[データスコープチェック]
    W --> X[部門削除]
    X --> Z
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 下位部門存在時削除禁止 | 下位部門が存在する部門は削除できない | 部門削除時 |
| BR-002 | ユーザー所属時削除禁止 | ユーザーが所属している部門は削除できない | 部門削除時 |
| BR-003 | 部門名一意性 | 同一親部門配下で部門名は一意でなければならない | 部門登録・更新時 |
| BR-004 | 自己参照禁止 | 自分自身を親部門にすることはできない | 部門更新時 |
| BR-005 | 停用時子部門チェック | 未停用の子部門が存在する場合、停用にできない | 部門更新時 |
| BR-006 | データスコープ制限 | ユーザーは自身のデータスコープ内の部門のみ操作可能 | 部門編集・削除時（管理者除く） |
| BR-007 | 祖先ID列維持 | 親部門変更時は自身と全子部門の祖先ID列を更新 | 部門更新時 |
| BR-008 | ルート部門保護 | 部門ID=100（若依科技）は特別扱いで親名称を「無」と表示 | 部門編集画面表示時 |

### 祖先ID列（ancestors）の仕様

- 部門の全ての祖先のIDをカンマ区切りで保持
- 例：`0,100,101` は ルート -> 100 -> 101 の階層を示す
- データ権限のSQLで`FIND_IN_SET`関数により部門階層での絞り込みに使用

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 部門一覧取得 | sys_dept | SELECT | 条件に合致する部門一覧を取得 |
| 部門登録 | sys_dept | INSERT | 新規部門情報を登録 |
| 部門更新 | sys_dept | UPDATE | 部門情報を更新 |
| 部門削除 | sys_dept | DELETE/UPDATE | 部門を削除（論理削除） |
| 下位部門数確認 | sys_dept | SELECT COUNT | 下位部門数を取得 |
| ユーザー所属確認 | sys_user | SELECT COUNT | 所属ユーザー数を取得 |
| 子部門祖先更新 | sys_dept | UPDATE | 子部門の祖先ID列を更新 |

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

#### sys_dept

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | dept_id | 自動採番 | 主キー |
| INSERT | parent_id | 入力値 | 親部門ID |
| INSERT | ancestors | 親の祖先 + 親ID | 祖先ID列 |
| INSERT | dept_name | 入力値 | 部門名 |
| INSERT | order_num | 入力値 | 表示順序 |
| INSERT | leader | 入力値 | 責任者 |
| INSERT | phone | 入力値 | 電話番号 |
| INSERT | email | 入力値 | メールアドレス |
| INSERT | status | 入力値または'0' | ステータス |
| INSERT | create_by | ログインユーザー名 | 作成者 |
| INSERT | create_time | sysdate() | 作成日時 |
| UPDATE | del_flag | '2' | 論理削除時 |
| SELECT | del_flag | '0' | 有効データのみ取得 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | バリデーションエラー | 必須項目未入力、形式不正 | エラーメッセージを画面表示 |
| - | 一意性エラー | 部門名が重複 | 「〜已存在」メッセージを表示 |
| - | 下位部門存在エラー | 下位部門が存在する状態で削除 | 「存在下级部门,不允许删除」を表示 |
| - | ユーザー所属エラー | ユーザーが所属している状態で削除 | 「部门存在用户,不允许删除」を表示 |
| - | 自己参照エラー | 自分自身を親部門に設定 | 「上级部门不能是自己」を表示 |
| - | 停用エラー | 未停用の子部門がある状態で停用 | 「该部门包含未停用的子部门！」を表示 |

### リトライ仕様

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

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

- 部門登録：単一INSERT処理
- 部門更新：部門情報更新 + 子部門祖先更新を1トランザクションで実行
- 部門削除：単一UPDATE処理（論理削除）

## パフォーマンス要件

- 部門一覧取得：ツリー構造のため全件取得

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

1. **権限制御**：Apache Shiroによるメソッドレベルの権限チェック
2. **データスコープ**：部門データへのアクセスは操作者のデータスコープに制限
3. **監査ログ**：@Logアノテーションによる操作ログ記録

## 備考

- 部門削除は論理削除（del_flag='2'）となる
- ancestors列はFIND_IN_SET関数での検索に最適化された形式
- 非管理者ユーザーの新規登録時は、自身の部門が親部門のデフォルト値となる

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | SysDept.java | `ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java` | 部門エンティティ、ancestors（祖先ID列）の役割を確認 |

**読解のコツ**: `ancestors`フィールド（28行目）が階層構造のキー。親部門変更時の更新処理に注目。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SysDeptController.java | `ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java` | 各エンドポイント、特に削除時のチェックロジックを確認 |

**主要処理フロー**:
- **45-52行目**: 部門一覧取得
- **69-84行目**: 新規登録保存
- **103-128行目**: 更新保存（自己参照・子部門チェック）
- **133-149行目**: 削除（下位部門・ユーザー所属チェック）

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | SysDeptServiceImpl.java | `ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java` | 祖先ID列の生成・更新ロジックを確認 |

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

```
SysDeptController
    │
    ├─ list() [部門一覧取得]
    │      └─ ISysDeptService.selectDeptList()
    │
    ├─ addSave() [部門新規登録]
    │      ├─ ISysDeptService.checkDeptNameUnique()
    │      └─ ISysDeptService.insertDept()
    │
    ├─ editSave() [部門更新]
    │      ├─ ISysDeptService.checkDeptDataScope()
    │      ├─ ISysDeptService.checkDeptNameUnique()
    │      ├─ ISysDeptService.selectNormalChildrenDeptById()
    │      └─ ISysDeptService.updateDept()
    │
    └─ remove() [部門削除]
           ├─ ISysDeptService.selectDeptCount()
           ├─ ISysDeptService.checkDeptExistUser()
           ├─ ISysDeptService.checkDeptDataScope()
           └─ ISysDeptService.deleteDeptById()
```

### 関連ファイル一覧

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