Files
windmill_workflow/docs/shiraou/19_windmill_通知ワークフロー連携仕様.md
2026-04-04 09:15:09 +09:00

9.1 KiB
Raw Blame History

Windmill 通知ワークフロー連携仕様

作成日: 2026-02-21 対象システム: 白皇集落営農組合 統合システム (shiraou.keinafarm.net) 目的: 予約・実績の変更をLINEで管理者に通知するWindmillワークフローの実装仕様


1. 概要

統合システム側が「変更履歴取得API」を提供する。 Windmillは定期的にこのAPIをポーリングし、変更があればLINEへ通知する。

Windmill定期実行
  └→ GET https://shiraou.keinafarm.net/reservations/api/changes/?since=<前回実行時刻>
        └→ 変更一覧(予約・実績)を取得
              └→ 変更があればLINE Messaging APIへ通知
                    └→ 前回実行時刻を更新

2. 変更履歴取得API

エンドポイント

GET https://shiraou.keinafarm.net/reservations/api/changes/

認証

X-API-Key ヘッダーにAPIキーを指定する統合システム管理者から取得

X-API-Key: <NOTIFICATION_API_KEY>

APIキーが不正な場合は 401 Unauthorized が返る。

クエリパラメータ

パラメータ 必須 説明
since ISO8601文字列 必須 この日時以降の変更を取得する

since の形式例:

  • 2026-02-21T10:00:00 ナイーブ、JSTとして扱われる
  • 2026-02-21T10:00:00+09:00 (タイムゾーン付き、推奨)

レスポンス200 OK

{
  "checked_at": "2026-02-21T12:00:00+09:00",
  "since": "2026-02-21T10:00:00+09:00",
  "reservations": [
    {
      "operation": "create",
      "reservation_id": 123,
      "user_name": "田中太郎",
      "machine_name": "トラクター",
      "start_at": "2026-02-25T09:00:00+09:00",
      "end_at": "2026-02-25T12:00:00+09:00",
      "operated_at": "2026-02-21T11:30:00+09:00",
      "operator_name": "田中太郎",
      "reason": ""
    },
    {
      "operation": "cancel",
      "reservation_id": 120,
      "user_name": "佐藤花子",
      "machine_name": "田植機",
      "start_at": "2026-02-22T08:00:00+09:00",
      "end_at": "2026-02-22T17:00:00+09:00",
      "operated_at": "2026-02-21T11:45:00+09:00",
      "operator_name": "佐藤花子",
      "reason": ""
    }
  ],
  "usages": [
    {
      "operation": "update",
      "usage_id": 456,
      "user_name": "山田次郎",
      "machine_name": "コンバイン",
      "amount": 4.0,
      "unit": "時間",
      "start_at": "2026-02-20T08:00:00+09:00",
      "end_at": "2026-02-20T12:00:00+09:00",
      "operated_at": "2026-02-21T11:55:00+09:00",
      "operator_name": "管理者A",
      "reason": "記録ミスのため修正"
    }
  ]
}

operation の値一覧

予約reservations:

意味
create 予約が作成された
update 予約の日時・機械が変更された
cancel 予約がキャンセルされた

実績usages:

意味
create 実績が登録された
update 実績が修正された
delete 実績が削除された

変更なしの場合

reservationsusages が両方空配列になる。通知は不要。

{
  "checked_at": "2026-02-21T12:05:00+09:00",
  "since": "2026-02-21T12:00:00+09:00",
  "reservations": [],
  "usages": []
}

エラーレスポンス

ステータス 原因
401 Unauthorized APIキーが不正または未設定
400 Bad Request since パラメータが欠落または不正な日時形式

3. Windmillワークフロー設計

3.1 スケジュール

  • 実行間隔: 5分毎*/5 * * * *
  • 農業機械の予約という用途上、数分の遅延は許容範囲

3.2 状態管理(前回実行時刻)

Windmillの Resource または State Variable に前回実行時刻を保存する。

変数名: last_checked_at
初期値: (初回実行時は現在時刻 - 10分 を使用)

3.3 ワークフロー全体フロー(擬似コード)

# 1. 前回実行時刻を取得
last_checked = get_state("last_checked_at") or (now() - 10 minutes)

# 2. 変更履歴を取得
response = GET "https://shiraou.keinafarm.net/reservations/api/changes/"
  params: { since: last_checked.isoformat() }
  headers: { "X-API-Key": NOTIFICATION_API_KEY }

# 3. 変更があればLINEに通知
if response.reservations or response.usages:
    message = format_line_message(response)
    send_line_message(LINE_CHANNEL_ACCESS_TOKEN, LINE_USER_ID, message)

# 4. 前回実行時刻を更新
set_state("last_checked_at", response.checked_at)

3.4 LINEメッセージのフォーマット例

def format_line_message(data):
    lines = ["📋 営農システム 変更通知\n"]

    for r in data["reservations"]:
        start = r["start_at"][:16].replace("T", " ")
        end   = r["end_at"][:16].replace("T", " ")

        if r["operation"] == "create":
            icon = "🟢"
            label = "予約作成"
        elif r["operation"] == "update":
            icon = "🔵"
            label = "予約変更"
        elif r["operation"] == "cancel":
            icon = "🔴"
            label = "予約キャンセル"

        lines.append(f"{icon} {label}")
        lines.append(f"  機械: {r['machine_name']}")
        lines.append(f"  利用者: {r['user_name']}")
        lines.append(f"  日時: {start}{end}")
        if r["reason"]:
            lines.append(f"  理由: {r['reason']}")
        lines.append("")

    for u in data["usages"]:
        start = u["start_at"][:16].replace("T", " ")

        if u["operation"] == "create":
            icon = "🟢"
            label = "実績登録"
        elif u["operation"] == "update":
            icon = "🔵"
            label = "実績修正"
        elif u["operation"] == "delete":
            icon = "🔴"
            label = "実績削除"

        lines.append(f"{icon} {label}")
        lines.append(f"  機械: {u['machine_name']}")
        lines.append(f"  利用者: {u['user_name']}")
        lines.append(f"  利用量: {u['amount']}{u['unit']}")
        lines.append(f"  日: {start[:10]}")
        if u["reason"]:
            lines.append(f"  理由: {u['reason']}")
        lines.append("")

    return "\n".join(lines).strip()

出力例:

📋 営農システム 変更通知

🟢 予約作成
  機械: トラクター
  利用者: 田中太郎
  日時: 2026-02-25 09:00 〜 2026-02-25 12:00

🔴 予約キャンセル
  機械: 田植機
  利用者: 佐藤花子
  日時: 2026-02-22 08:00 〜 2026-02-22 17:00

🔵 実績修正
  機械: コンバイン
  利用者: 山田次郎
  利用量: 4.0時間
  日: 2026-02-20
  理由: 記録ミスのため修正

4. Windmill側の環境変数シークレット

変数名 説明 設定場所
NOTIFICATION_API_KEY 統合システムのAPIキー Windmill Secret
LINE_CHANNEL_ACCESS_TOKEN LINE Messaging API チャネルアクセストークン Windmill Secret
LINE_USER_ID / LINE_GROUP_ID 通知先のユーザーIDまたはグループID Windmill Secret

5. 統合システム側の設定django側の作業

本番サーバーの docker-compose.yml に以下の環境変数を追加し、デプロイする。

environment:
  - NOTIFICATION_API_KEY=<任意の強いランダム文字列>

APIキーの生成例:

openssl rand -hex 32

6. 動作確認方法

curlで直接テスト

# 変更なし直近1分
curl -H "X-API-Key: <キー>" \
  "https://shiraou.keinafarm.net/reservations/api/changes/?since=2026-02-21T11:59:00%2B09:00"

# 広い範囲で変更を取得(初期確認用)
curl -H "X-API-Key: <キー>" \
  "https://shiraou.keinafarm.net/reservations/api/changes/?since=2026-01-01T00:00:00"

# APIキーなし → 401が返ることを確認
curl "https://shiraou.keinafarm.net/reservations/api/changes/?since=2026-01-01T00:00:00"

ダッシュボードで変更確認

管理者アカウントで https://shiraou.keinafarm.net/reservations/dashboard/ にアクセスすると、 直近30件の予約操作履歴・実績操作ログを確認できる。 Windmillワークフローのデバッグ時に「APIが何を返すべきか」の確認に使える。


7. 注意事項

  • since に渡す時刻は 前回の checked_at を使う(since ではなく checked_at を保存すること)
  • 同一の変更が2回通知されないよう、状態管理を確実に行う
  • ワークフローがエラーで終了した場合、last_checked_at を更新しないようにすること(次回実行時に再取得される)
  • APIキーは定期的にローテーションすること変更時は統合システム側の環境変数も同時に更新