0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python × Dify × RAGで学ぶ業務システム開発入門【第3回 ER図・画面設計編】

0
Last updated at Posted at 2026-06-17

はじめに

前回は、業務の現状分析から要求定義・要件定義までを行いました。

前回の記事:Python × Dify × RAGで学ぶ業務システム開発入門【第2回 要求定義・要件定義編】

第3回では、定義した機能要件をテーブル構造(ER図)と画面設計に落とし込みます。

本連載で使用するソースコードは、以下のGitHubリポジトリで公開しています。

aiota-jp/human-resource-ai

コードを書き始める前に設計を固めることには、明確な理由があります。

ER図を先に設計する理由
  → テーブル構造が決まれば、必要なデータとSQLが見える
  → 後からカラムを変更すると、既存コードへの影響が大きい

画面設計を先に行う理由
  → 画面が決まれば、必要なURLとデータの流れが見える
  → 実装途中で画面構成や操作方法がぶれるのを防げる

本記事で学ぶこと

項目 内容
ER図 テーブル、カラム、リレーションの整理
画面一覧 必要な画面、URL、HTTPメソッド、権限の整理
画面遷移図 画面間の移動ルールの整理
ワイヤーフレーム 主要画面のレイアウト設計

ER図とは何か

ER図(Entity-Relationship Diagram)とは、データベースのテーブル構造と関係性を図で表したものです。

Entity(エンティティ)         → テーブル
Relationship(リレーション)   → テーブル間の関係
Attribute(属性)              → テーブルのカラム

ER図を読み書きできるようになると、次のような判断をしやすくなります。

  • データをどのテーブルへ保存するか
  • どのテーブル同士をJOINするか
  • どこに外部キーを配置するか
  • 1対多や多対多の関係をどのように表現するか

テーブル設計

第2回で整理した機能要件をもとに、必要なテーブルを設計します。

テーブル一覧

本システムでは、次の8テーブルを使用します。

テーブル名 概要
user ログインユーザー名、ハッシュ化済みパスワード、ロールを管理する
department 人事部・開発部・営業部などの部署名を管理する
employee 社員番号、氏名、部署、役職、入社日などを管理する
training 研修名、開始日、終了日、研修内容を管理する
training_enrollment 社員と研修の申込関係、受講状態を管理する
training_history 出席率、理解度、課題スコアなどの研修実績を管理する
evaluation 社員の総合スコアとAI評価コメントを履歴として管理する
daily_report 社員の日報の日付、作業内容、メモを管理する

departmentとtraining_enrollmentはデータベースに作成されますが、現在の画面・サービス処理では十分に利用されていません。

  • employee.departmentには部署名を文字列として直接保存しており、department.idへの外部キーは設定されていません。
  • 研修履歴画面には有効な社員がすべて表示され、training_enrollmentを使った受講者の絞り込みは行っていません。

各テーブルの詳細設計

user(ログインユーザー)

カラム名 型 制約 説明
id INTEGER PK / AUTOINCREMENT 主キー
username TEXT NOT NULL / UNIQUE ログイン時に使用するユーザー名
password TEXT NOT NULL ハッシュ化済みパスワード
role TEXT NOT NULL / DEFAULT 'user' 権限(admin / staff / user)
created_at DATETIME DEFAULT CURRENT_TIMESTAMP 作成日時

passwordカラムには平文のパスワードを保存しません。werkzeug.security.generate_password_hash()でハッシュ化してから保存します。

department(部署マスタ)

カラム名 型 制約 説明
id INTEGER PK / AUTOINCREMENT 主キー
dept_name TEXT NOT NULL / UNIQUE 部署名
created_at DATETIME DEFAULT CURRENT_TIMESTAMP 作成日時

初期データとして、人事部・開発部・営業部が登録されます。ただし、部署を登録・編集・削除する画面は実装されていません。

employee(社員マスタ)

カラム名 型 制約 説明
id INTEGER PK / AUTOINCREMENT 主キー
employee_no TEXT NOT NULL / UNIQUE 社員番号
name TEXT NOT NULL 氏名
department TEXT 部署名
position TEXT 役職
hire_date DATE 入社日
is_active INTEGER NOT NULL / DEFAULT 1 有効フラグ
created_at DATETIME DEFAULT CURRENT_TIMESTAMP 作成日時
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP 更新日時

社員の削除ではレコードを物理的に削除せず、is_activeを0へ変更します。これにより、社員一覧には表示しない一方で、既存の研修履歴や評価データを残せます。

training(研修マスタ)

カラム名 型 制約 説明
id INTEGER PK / AUTOINCREMENT 主キー
training_name TEXT NOT NULL 研修名
start_date DATE 開始日
end_date DATE 終了日
description TEXT 研修内容
created_at DATETIME DEFAULT CURRENT_TIMESTAMP 作成日時

training_enrollment(研修申込)

カラム名 型 制約 説明
id INTEGER PK / AUTOINCREMENT 主キー
employee_id INTEGER NOT NULL / FK → employee.id 社員ID
training_id INTEGER NOT NULL / FK → training.id 研修ID
status TEXT DEFAULT 'enrolled' 受講状態
created_at DATETIME DEFAULT CURRENT_TIMESTAMP 作成日時

employee_idとtraining_idの組み合わせにはUNIQUE制約があり、同じ社員と研修の組み合わせが重複して登録されるのを防ぎます。

現在は、training_enrollmentへ登録する画面やサービス処理は実装されていません。今後、研修ごとの受講者を選択する機能を追加するときに利用できます。

training_history(研修受講履歴)

カラム名 型 制約 説明
id INTEGER PK / AUTOINCREMENT 主キー
employee_id INTEGER NOT NULL / FK → employee.id 社員ID
training_id INTEGER NOT NULL / FK → training.id 研修ID
attendance_rate REAL DEFAULT 0.0 出席率
understanding_level INTEGER DEFAULT 0 理解度スコア
report_score INTEGER DEFAULT 0 課題・レポートスコア
created_at DATETIME DEFAULT CURRENT_TIMESTAMP 作成日時

employee_idとtraining_idの組み合わせにはUNIQUE制約があります。研修履歴の保存時には、組み合わせが存在しなければINSERTし、存在する場合はUPDATEします。

出席率は日別の出欠データから自動計算するのではなく、研修履歴画面またはExcel・CSVファイルから数値として登録します。

evaluation(評価)

カラム名 型 制約 説明
id INTEGER PK / AUTOINCREMENT 主キー
employee_id INTEGER NOT NULL / FK → employee.id 評価対象の社員ID
score REAL 総合スコア
ai_comment TEXT AI評価コメント
created_at DATETIME DEFAULT CURRENT_TIMESTAMP 評価日時

社員ごとに複数の評価を保存できます。評価一覧では、各社員の最新評価を表示します。

総合スコアは、社員ごとの研修実績の平均値から次の式で計算します。

総合スコア
= 出席率 × 30%
+ 理解度 × 40%
+ 課題スコア × 30%

daily_report(日報)

カラム名 型 制約 説明
id INTEGER PK / AUTOINCREMENT 主キー
employee_id INTEGER NOT NULL / FK → employee.id 対象社員ID
report_date DATE NOT NULL 日報の日付
content TEXT 作業内容
memo TEXT 所感・メモ
created_at DATETIME DEFAULT CURRENT_TIMESTAMP 作成日時

通常のadmin・staff・userアカウントでは、日報画面で社員を選択して登録済みの日報を一覧表示します。一方、社員番号と同じユーザー名でログインした社員アカウントでは、ログイン中の社員IDが自動設定され、本人の日報だけを登録・表示します。

ER図(全体)

実装されている外部キー関係をER図で表すと、次のようになります。

USERとDEPARTMENTは、現在の実装ではほかのテーブルと外部キーで接続されていません。ただし認証時には、user.usernameとemployee.employee_noを動的に照合します。一致する有効な社員が見つかった場合は、その社員IDと氏名をセッションへ保存し、社員専用マイページを表示します。

たとえば初期ユーザーEMP001は、社員番号EMP001の「山田 太郎」と照合されます。これはデータベース上の外部キーではなく、認証処理内のLEFT JOINによる論理的な関連付けです。

リレーションの読み方

関係 説明
employee 1 → 多 training_enrollment 1人の社員が複数の研修へ申し込める
training 1 → 多 training_enrollment 1件の研修に複数の社員が申し込める
employee 1 → 多 training_history 1人の社員が複数の研修履歴を持てる
training 1 → 多 training_history 1件の研修に複数社員の実績を保存できる
employee 1 → 多 evaluation 1人の社員について複数回の評価を保存できる
employee 1 → 多 daily_report 1人の社員が複数の日報を持てる

画面設計

テーブル設計が終わったら、次は画面設計です。「どのような画面が必要か」「画面間をどのように移動するか」を整理します。

画面一覧

画面名 URL HTTPメソッド 主な操作 権限
ログイン /login GET / POST ユーザー名・パスワードによる認証 未ログインユーザー
ログアウト /logout GET セッションを破棄してログイン画面へ移動 全ロール
ダッシュボード / GET 利用可能な機能への移動。社員連携済みの場合はマイページへ移動 全ロール
社員マイページ /my-page GET 本人の社員情報、研修履歴、最新評価、直近の日報を確認 社員情報と連携したログインユーザー
社員一覧 /employees GET 一覧表示、社員番号・氏名・部署による検索 staff / admin
社員登録 /employees/new GET / POST 社員情報の登録 staff / admin
社員編集 /employees/<int:employee_id>/edit GET / POST 社員情報の編集 staff / admin
社員削除 /employees/<int:employee_id>/delete POST 社員の論理削除 admin
研修一覧 /trainings GET 研修一覧の表示 staff / admin
研修登録 /trainings/new GET / POST 研修情報の登録 staff / admin
研修履歴 /trainings/<int:training_id>/history GET / POST 出席率・理解度・課題スコアの入力 staff / admin
評価管理 /evaluations GET 集計値、総合スコア、AIコメントの表示 staff / admin
AIコメント生成 /evaluations/<int:employee_id>/generate POST 生成中UIを表示し、AI評価コメントを生成・保存 staff / admin
Excel取込 /import GET / POST 社員台帳・研修結果の取込 staff / admin
Excel出力 /export GET 評価一覧のExcelダウンロード staff / admin
日報管理 /reports GET / POST 日報の登録・一覧表示。社員連携済みユーザーは本人分だけを扱う 全ロール
社内文書検索 /search GET / POST Difyを利用した社内文書検索 全ロール
FAQチャット /chat GET チャット画面の表示 全ロール
FAQメッセージ送信API /chat/send POST 質問の送信とJSON形式の回答取得 全ロール

/reports/newというURLも定義されていますが、日報登録画面を個別に表示するのではなく、/reportsへリダイレクトします。日報の登録は/reportsへのPOSTで行います。

画面遷移図

画面間の主な移動ルールを整理します。

[ログイン]
    ├── admin / staff / user ──> [ダッシュボード]
    └── 社員番号アカウント(例:EMP001) ──> [社員マイページ]

[ダッシュボード]
    ├── [社員一覧]
    │      ├── [社員登録] ── 登録完了 ──> [社員一覧]
    │      ├── [社員編集] ── 更新完了 ──> [社員一覧]
    │      └── 社員削除 ── 削除完了 ──> [社員一覧]
    │
    ├── [研修一覧]
    │      ├── [研修登録] ── 登録完了 ──> [研修一覧]
    │      └── [研修履歴] ── 保存完了 ──> [研修履歴]
    │
    ├── [評価管理]
    │      ├── AIコメント生成 ── 生成中表示 ──> [評価管理]
    │      └── Excel出力 ──> ファイルダウンロード
    │
    ├── [Excel取込] ── 取込完了 ──> [Excel取込]
    ├── [日報管理] ── 登録完了 ──> [日報管理]
    ├── [社内文書検索] ──> 同じ画面に検索結果を表示
    └── [FAQチャット] ──> 同じ画面に質問と回答を追加

各画面のナビゲーション
    └── ログアウト ──> [ログイン]

[社員マイページ]
    ├── 本人の社員情報・研修履歴・最新評価・直近5件の日報を確認
    ├── [日報管理] ── 本人の日報だけを登録・表示
    ├── [社内文書検索]
    └── [FAQチャット]

userロールのダッシュボードには、社員管理・研修管理・評価管理・Excel入出力は表示されません。これらのURLへ直接アクセスした場合も、roles_required()によってアクセスが拒否されます。

初期ユーザーEMP001(パスワード:password)もロールはuserですが、ユーザー名が社員番号と一致するため、ログイン後は/my-pageへ移動します。/へアクセスした場合も社員マイページへリダイレクトされます。

ワイヤーフレーム(主要画面)

実際の開発では、Figmaなどのツールを使用して画面レイアウトを設計する方法もあります。本記事では、主要画面の完成イメージを確認します。

ダッシュボード

ダッシュボード

社員一覧

社員一覧

社員登録・編集フォーム

社員登録・編集フォーム

評価一覧・AIコメント生成

評価一覧・AIコメント生成

「AI生成」または「再生成」ボタンを押すと、押したボタンの表示が「生成中...」に切り替わり、画面中央にローディング表示が出ます。処理中はすべての生成ボタンを無効化し、Dify APIへの二重送信を防ぎます。生成が完了すると評価一覧へ戻り、保存されたコメントを表示します。

社員専用マイページ

社員番号アカウントでログインした社員向けの画面です。本人の基本情報、研修履歴、最新評価、直近5件の日報を1画面で確認できます。

社内文書検索(RAG)

社内文書検索

設計をコードに反映する

ER図と画面設計が決まれば、実装の見通しが立ちます。設計と実装の対応関係を確認します。

テーブル設計 → database.py

次のコードでは、研修履歴テーブルに社員と研修への外部キーを設定しています。また、同じ社員と研修の組み合わせが重複しないようにUNIQUE制約を設定しています。

# database.py(抜粋)
CREATE TABLE IF NOT EXISTS training_history (
    id                  INTEGER PRIMARY KEY AUTOINCREMENT,
    employee_id         INTEGER NOT NULL REFERENCES employee(id),
    training_id         INTEGER NOT NULL REFERENCES training(id),
    attendance_rate     REAL    DEFAULT 0.0,
    understanding_level INTEGER DEFAULT 0,
    report_score        INTEGER DEFAULT 0,
    created_at          DATETIME DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(employee_id, training_id)
);

画面設計 → app.pyのルーティング

URL、HTTPメソッド、パラメーター、権限をFlaskのルーティングへ反映します。

@app.route("/employees")
@login_required
@roles_required("staff", "admin")
def employee_list():
    ...


@app.route("/employees/<int:employee_id>/edit", methods=["GET", "POST"])
@login_required
@roles_required("staff", "admin")
def employee_edit(employee_id):
    ...


@app.route("/employees/<int:employee_id>/delete", methods=["POST"])
@login_required
@roles_required("admin")
def employee_delete(employee_id):
    ...


@app.route("/trainings/<int:training_id>/history", methods=["GET", "POST"])
@login_required
@roles_required("staff", "admin")
def training_history(training_id):
    ...


@app.route("/chat/send", methods=["POST"])
@login_required
def chat_send():
    ...


@app.route("/my-page")
@login_required
def employee_portal():
    employee_id = session.get("employee_id")
    if not employee_id:
        return redirect(url_for("index"))
    ...

画面設計 → templates/のHTMLファイル

templates/
├── base.html               ← 共通レイアウト・ナビゲーション
├── login.html              ← ログイン画面
├── index.html              ← ダッシュボード
├── employee_portal.html    ← 社員専用マイページ
├── employee_list.html      ← 社員一覧・検索
├── employee_form.html      ← 社員登録・編集
├── training_list.html      ← 研修一覧
├── training_form.html      ← 研修登録
├── training_history.html   ← 研修履歴入力
├── evaluation.html         ← 評価一覧・AIコメント生成
├── import.html             ← Excel・CSV取込
├── report.html             ← 日報登録・一覧
├── search.html             ← 社内文書検索
└── chat.html               ← FAQチャット

現在の設計で今後改善できる点

今回のコードは、ローカルで業務システム開発を学ぶためのサンプルです。実務システムへ発展させる場合は、次の改善が考えられます。

改善項目 現在の実装 改善案
部署管理 社員テーブルに部署名を文字列で保存 department_idを追加して部署マスタと外部キーで接続する
受講者管理 研修履歴画面に有効な社員をすべて表示 training_enrollmentを利用して研修ごとの受講者を管理する
出席管理 出席率を数値で直接入力 日別の出欠データを保存し、出席率を自動計算する
ユーザーと社員の関連付け usernameとemployee_noを認証時に照合 employee_idを外部キーとして保持し、明示的に関連付ける
日報の権限制御 社員連携済みユーザーは本人分のみ、その他のアカウントは全社員分を扱う 担当者の部署・担当範囲に応じた閲覧制御を追加する
RAGの参照元 Difyの回答本文のみを表示 文書名、章、ページ、スコアなどを表示する
FAQの応答方式 blockingモード ストリーミング応答に対応する

おわりに

第3回では、ER図と画面設計を通じて、実装の土台を整理しました。

ポイントを振り返ります。

  • テーブル設計:8テーブルのカラム、制約、役割を整理した
  • ER図:実装済みの外部キー関係と、独立しているテーブルを確認した
  • 画面一覧:画面、URL、HTTPメソッド、ロール別権限を整理した
  • 画面遷移図:通常ユーザーと社員番号アカウントで異なるログイン後の遷移を整理した
  • ワイヤーフレーム:主要画面のレイアウトを確認した
  • 操作中のUI:AIコメント生成中の表示と二重送信防止を確認した
  • 実装との対応:database.py、app.py、templates/との関係を確認した

次回は**「Flaskログイン機能編」**として、ログイン・ログアウト、セッション管理、パスワードのハッシュ化、ロール別アクセス制御を解説します。

次回:Python × Dify × RAGで学ぶ業務システム開発入門【第4回 Flaskログイン機能編】

本連載について

本連載の各記事で実装する内容は次のとおりです。

回 タイトル 対応画面 主な内容
第1回 業務システム全体設計編 ― システム概要・技術選定・アーキテクチャ
第2回 要求定義・要件定義編 ― 業務分析・機能要件・非機能要件
第3回 ER図・画面設計編 ― テーブル設計・画面遷移図・ワイヤーフレーム
第4回 Flaskログイン機能編 🔐 ログイン セッション・ハッシュ・ロール認可・社員番号ログイン
第5回 社員管理CRUD編 👤 社員管理 一覧・検索・登録・更新・論理削除・入力制御
第6回 研修管理編 📚 研修管理 研修登録・LEFT JOIN・履歴一括入力・UPSERT
第7回 Excel業務自動化編 📥 取込 / 📤 出力 pandas取込・openpyxl出力
第8回 Dify API連携編 📊 評価管理 API・フォールバック・生成中UI・二重送信防止
第9回 RAG構築編 🔍 文書検索 Dify Chat API・質問送信・回答表示・エラー処理
第10回 FAQチャットボット編 💬 FAQチャット 会話ID・Fetch API・生成中表示・二重送信防止
公開編 Ubuntu VPS・Docker公開編 🌐 サービス公開 Ubuntu VPS・Docker・Nginxを使った本番環境構築とWebアプリケーション公開

参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?