From 44ee8781a91669ea041d973a1e0544607b21c561 Mon Sep 17 00:00:00 2001 From: Kenichiro NOGI Date: Fri, 11 Sep 2026 18:02:17 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20Bot=E3=83=88=E3=83=BC=E3=82=AF=E3=83=95?= =?UTF-8?q?=E3=83=AD=E3=83=BC=E5=86=8D=E8=A8=AD=E8=A8=88=E3=81=AE=E5=AE=9F?= =?UTF-8?q?=E8=A3=85=E5=AE=8C=E4=BA=86=E3=82=92=E8=A8=98=E9=8C=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docs/n8n/2026-09-10-bot-flow-redesign-plan.md | 89 +- .../docs/n8n/design-fixed-menu.md | 1277 +++++++++++++++++ 2 files changed, 1363 insertions(+), 3 deletions(-) create mode 100644 Pleasanter/健康診断管理/docs/n8n/design-fixed-menu.md diff --git a/Pleasanter/健康診断管理/docs/n8n/2026-09-10-bot-flow-redesign-plan.md b/Pleasanter/健康診断管理/docs/n8n/2026-09-10-bot-flow-redesign-plan.md index 952f6c05..7a6eb445 100644 --- a/Pleasanter/健康診断管理/docs/n8n/2026-09-10-bot-flow-redesign-plan.md +++ b/Pleasanter/健康診断管理/docs/n8n/2026-09-10-bot-flow-redesign-plan.md @@ -2,6 +2,8 @@ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. +**2026-09-11更新**: 着手前に本番HC-WAへ2件の緊急バグ修正が入った(下記Global Constraints・Task0参照)。全ワークフローは現在誤送信対策で`inactive`化されている。本計画のPUT作業自体はactive/inactiveに関係なく実施できるが、Task2/3/4の「実機トークで確認」ステップの直前に対象ワークフローを一時activateする判断をユーザーに仰ぐこと。 + **Goal:** 健康診断管理フェーズ2のLINEWORKS Bot対話フローを、承認済み仕様書(`2026-09-10-bot-flow-redesign.md`)通りに再構築する。「健康診断メニュー」の呼称統一、パターンA(受診日時)の分岐変更、パターンB(受診結果確認)の新規実装が中心。 **Architecture:** n8n上で本番稼働中の4ワークフロー(HC-WA/HC-WP/HC-REMINDER/HC-DOC-TIMEOUT)のCodeノードを、Public API(`PUT /api/v1/workflows/{id}`)経由で直接更新する運用。508971(社員別健康診断管理)側は新規Process「自分手配」を追加。DescriptionBの「日時:」行書き換えはn8n(HC-WA)側で行う(Pleasanter Processの値式では動的な文字列組み立てが難しいため)。 @@ -10,6 +12,8 @@ ## Global Constraints +- **【2026-09-11反映済み・消さないこと】HC-WA「署名検証・対象レコード特定」ノード174行目付近**: `resultId: record.ResultId` は `resultId: String(record.ResultId)` に修正済み。resultIdがNumber型のまま渡ると、後段「IF: resultIdあり」ノード(`typeValidation:strict`のstring.notEmpty演算子)が型エラーでワークフロー全体を異常終了させ、Webhookレスポンス未返却→LINEWORKS側リトライ→同一メッセージ重複送信を引き起こすバグの修正。Task2〜4はこのノードを触らないので通常は影響しないが、万一このノードのコードを再取得・再構築する場合は必ずString化を維持すること。 +- **【2026-09-11反映済み・消さないこと】HC-WA「分岐処理・実行・送信」ノード冒頭、`// ==== -1. LINEWORKS自動送信イベント(トーク開始時の"利用開始"等) → 無反応 ====`ブロック(現状142-145行目)**: LINEWORKSがBotとのトーク開始・グループ招待時に自動送信する定型テキスト`"利用開始"`を、ユーザーの実発言と誤認識して無条件応答(「対象の健診情報がありません」「待機状態が不正です」等)を返してしまうバグの修正。対応する「署名検証・対象レコード特定」側の`content.text === "利用開始"`検知ブロックとセット。Task2〜4で「分岐処理・実行・送信」ノードのjsCodeを書き換える際、必ずGET直後の最新コードをベースにし(このブロックを含んだ状態から差分編集する)、コミット済みの古いコードスニペットで丸ごと上書きしないこと。 - n8n CodeノードはTask Runner環境。`require(...)`でNode.js組み込みモジュールは読めない。グローバルの`crypto`も存在しない(暗号処理が要る場合はpure JS実装で対応、`identity.js`の署名検証で実績あり)。 - Pleasanter `View.ColumnFilterHash`は列の型で書式が変わる。**リンク型(選択肢が`[[SiteId]]`や`[[Users*]]`)の列は値をJSON配列文字列化(`JSON.stringify([value])`)、通常の自由入力Class列は素の文字列**を渡す。508971の`ClassC`はリンク型(配列形式)、504412の`ClassA`/`Class011`は自由入力(文字列形式)。書式を誤るとフィルタが機能せず全件返る(サイレント障害になるので要注意)。 - 508971のProcess定義をAPI経由で**ゼロから構築してPUTすると、個別レコード編集画面が全件開けなくなる重大障害**が過去に発生している。新規Process追加時は、Pleasanter画面上で管理者に空のProcessを1件手動登録してもらい、それに対してAPI経由でプロパティを追記する方式のみ許可する。既存Processesを配列ごと構築し直すことは絶対にしない。 @@ -56,6 +60,25 @@ n8n側は全てPublic API経由のPUT更新。ローカルには一時作業フ **Interfaces:** - Produces: DescriptionBの「日時:」行置換関数`replaceDateLineInDescription(description, newDateText)`の実装(Task 2以降で使う) +- [ ] **Step 0: 2026-09-11緊急修正がHC-WA本番に反映済みであることを確認する** + +```bash +node -e " +const KEY = ''; +(async () => { + const res = await fetch('https://n8n32.next-hd.net/api/v1/workflows/0i0Ze3Gq0Waof3Fa', { headers: { 'X-N8N-API-KEY': KEY } }); + const j = await res.json(); + const seimei = j.nodes.find(n => n.name === '署名検証・対象レコード特定').parameters.jsCode; + const bunki = j.nodes.find(n => n.name === '分岐処理・実行・送信').parameters.jsCode; + console.log('active:', j.active); + console.log('resultId String化:', seimei.includes('resultId: String(record.ResultId)')); + console.log('利用開始イベント無視:', bunki.includes('identity.resolveError === \"system_event\"')); +})(); +" +``` + +Expected: `active: false`(HC-WAはユーザー指示で誤送信対策のため停止中)、両方の判定行が`true`。`false`が出た場合は他セッションで別のPUTが入って巻き戻った可能性があるため、作業前に必ずユーザーへ確認する。 + - [ ] **Step 1: 513409のDescriptionBを取得し、実際の改行・文字コードを確認する** ```bash @@ -309,7 +332,9 @@ const fs = require('fs'); - [ ] **Step 2: `_branch_next.js`の該当箇所を書き換える** -180-191行目(旧): +**注意**: 下記の行番号(185-196行目)は2026-09-11時点の本番コード基準。Step1で取得した`_branch_next.js`は先頭に2026-09-11追加の「-1. LINEWORKS自動送信イベント」ブロック(9行)を含むため、行番号でなく下記コード文字列で該当箇所を検索して置換すること。このブロック自体(`identity.resolveError === "system_event"`の判定)と、その前の「0. 対象者解決エラー」ブロックは一切変更しない。 + +185-196行目相当(旧): ```javascript if (text === "健康診断について") { const categoryActions = []; @@ -364,6 +389,22 @@ const fs = require('fs'); Expected: `200` とタイムスタンプ。 +- [ ] **Step 3.5: HC-WAを一時activateする(ユーザー確認必須)** + +現在HC-WAは誤送信対策で`inactive`。実機トーク確認にはWebhookが動く必要があるため、activateしてよいかユーザーに確認してから実施する: + +```bash +node -e " +const KEY = ''; +(async () => { + const res = await fetch('https://n8n32.next-hd.net/api/v1/workflows/0i0Ze3Gq0Waof3Fa/activate', { method: 'POST', headers: { 'X-N8N-API-KEY': KEY } }); + console.log((await res.json()).active); +})(); +" +``` + +Expected: `true`。以降のタスクでも実機確認の間はactiveなままでよい(Task6完了まで継続)。 + - [ ] **Step 4: 野木さん(513409、現在Status=310)の実機トークで確認する** 固定メニュー「健康診断について」をタップし、「受診日時について」「受診結果について」の2択(Status=310なので両方出る)が表示されることを確認する。まだ中身は未実装なので、押しても`if`に一致せず`buildResult("noop")`扱いになる(何も返らない)想定。ここでは**ボタンの見た目だけ**確認する。 @@ -373,7 +414,7 @@ Expected: `200` とタイムスタンプ。 ## Task 3: HC-WA branch.js — 受診日時について(パターンA) **Files:** -- Modify: 同上ノード。193-214行目(旧`健診日程`ブロック)と244-280行目(`schedule_confirm`状態)を全面改修。 +- Modify: 同上ノード。旧`健診日程`ブロック(Task2時点で198-219行目相当)と`schedule_confirm`状態(249-285行目相当)を全面改修。**行番号は毎回Step1で取得する最新コードに合わせて実装者が特定すること**(2026-09-11追加の冒頭ブロック分だけ既存計画記載よりずれている)。 **Interfaces:** - Consumes: Task1で追加したProcessId(`自分手配`、`PROCESS_ID.SELF_ARRANGE`として定義)、Task0で検証した`replaceDateLineInDescription`/`formatDateTimeSlash` @@ -638,7 +679,7 @@ cd "/c/Users/k.nogi/#GitHub/ken_nogi/dev" && rm -f _wf_hcwa.json _branch_next.js ## Task 4: HC-WA branch.js — 受診結果について(パターンB) **Files:** -- Modify: 同上ノード。216-239行目(旧`受診・再検査報告`/`診断書提出`ブロック)、301-309行目(`attendance_310`のyesno_confirm処理)を全面改修。新規状態`retest_choice`・`reschedule_input`を追加。 +- Modify: 同上ノード。旧`受診・再検査報告`/`診断書提出`ブロック(Task2時点で221-244行目相当)、`attendance_310`のyesno_confirm処理(306-314行目相当)を全面改修。新規状態`retest_choice`・`reschedule_input`を追加。**行番号は毎回Step1で取得する最新コードに合わせて実装者が特定すること**(2026-09-11追加の冒頭ブロック分だけ既存計画記載よりずれている)。 **Interfaces:** - Consumes: Task3の`buildHospitalContactMessage`、`replaceDateLineInDescription`、`formatDateTimeSlash`、`parseDateInput` @@ -947,8 +988,50 @@ git commit -m "docs(healthcheck): Botトークフロー再設計の実装完了 --- +## Task 7: 全Webhookの本番反映判断 + +Task1〜6完了後、健康診断関連の全Webhook(HC-WA/HC-SETUP/HC-FORM-SYNC/HC-WP、いずれも2026-09-11時点で`inactive`)を本番稼働状態に戻すかどうかの最終判断。 + +**Files:** なし(n8n active状態の変更のみ) + +- [ ] **Step 1: 健康診断関連ワークフロー全件のactive状態を一覧確認する** + +```bash +node -e " +const KEY = ''; +(async () => { + const res = await fetch('https://n8n32.next-hd.net/api/v1/workflows?limit=100', { headers: { 'X-N8N-API-KEY': KEY } }); + const j = await res.json(); + for (const w of j.data) if (w.name.startsWith('HC-')) console.log(w.id, w.name, 'active=', w.active); +})(); +" +``` + +- [ ] **Step 2: ユーザーへ本番反映(activate)可否を確認する** + +Task1〜6の実機検証(野木さん513409、全シナリオ)がすべて成功していることを前提に、「HC-WA/HC-SETUP/HC-FORM-SYNC/HC-WPを本番activateしてよいか」をユーザーへ確認する。HC-REMINDER/HC-DOC-TIMEOUTは元々2026-09-07時点から「実運用開始タイミングで別途判断」の対象(このタスクのスコープ外、ユーザーが別途判断)。 + +- [ ] **Step 3: 承認を得たワークフローのみactivateする** + +```bash +node -e " +const KEY = ''; +const ids = ['0i0Ze3Gq0Waof3Fa', '0jVVF9WGlZrAygXX', 'G2nWfvPCsIKpup60', 'swXfpoDtwZDT3Hwk']; // HC-WA/HC-SETUP/HC-FORM-SYNC/HC-WP +(async () => { + for (const id of ids) { + const res = await fetch(\`https://n8n32.next-hd.net/api/v1/workflows/\${id}/activate\`, { method: 'POST', headers: { 'X-N8N-API-KEY': KEY } }); + const j = await res.json(); + console.log(j.name, 'active=', j.active); + } +})(); +" +``` + +--- + ## Self-Review メモ - Task0で洗い出した4項目は、それぞれTask1 Step4(Process起点Status)、Task3 Step1-3(DescriptionB置換・ResultIdフィルタ書式)、Task4 Step6(ProcessId無し更新の可否)で実際に検証・反映される導線を確保した。 - 診断書保存フロー・再検査フローのa〜d収集ロジックは仕様書通り「既存のまま変更なし」のため、本計画にタスクを設けていない(意図的な除外)。 - HC-DOC-TIMEOUTワークフローは今回変更対象外(仕様書に変更点なし)。 +- **2026-09-11追加分**: 着手前にresultId型エラー・"利用開始"イベント無条件応答の2バグを本番HC-WAへ緊急修正済み(Global Constraints・Task0 Step0参照)。この2件のロジックはTask2〜4の改修対象範囲(「健康診断について」以降の分岐)より前段に位置するため、Step1のGET→差分編集という手順を守る限り自然に保持される。Task7で本番activateの最終判断ステップを新設(旧計画にはなかった、全Webhook停止状態からの再開手順が欠けていたため)。 diff --git a/Pleasanter/健康診断管理/docs/n8n/design-fixed-menu.md b/Pleasanter/健康診断管理/docs/n8n/design-fixed-menu.md new file mode 100644 index 00000000..7158d35b --- /dev/null +++ b/Pleasanter/健康診断管理/docs/n8n/design-fixed-menu.md @@ -0,0 +1,1277 @@ +# 健康診断Bot 対話メニュー機能 設計メモ(2026-09-06) + +508971(社員別健康診断管理)× LINEWORKS Bot連携で、3機能(健診日程/受診・再検査報告/ +診断書提出)を追加する設計。既存の`healthcheck-lineworks-survey-n8n-design.md` +(HC-SUB/HC-WA/HC-WPの基本構成)を前提に、その上に対話メニュー方式を追加する。 + +**ストレスチェックは対象範囲外(2026-09-07確定)**: 508971の`CheckB`(SC受診済)・ +`Class056`(SC判定)は項目として残すが、運用(誰がいつどう更新するか)が未確定のため、 +今回のBot対話メニュー機能の対象から除外する。以下の本文中に4択・4機能としてストレス +チェックが含まれる記述が残っている場合は履歴であり、実装は3択・3機能で進める。 + +**自分手配(社員が病院を自分で予約するケース)は仕様未確定・今回の対象範囲外 +(2026-09-07追加)**: 検査機関の選択肢(`Class003`、SiteId 511924「受診病院一覧」)へ +「自分手配」を追加済み(ResultId 513480)。ただし詳細仕様(日程の入力方法・Bot対話フロー等)は +未確定のため、Class003=「自分手配」のレコードは①日程通知(Status 250→300、候補日Date001を +前提とした案内)の対象から外す。自分手配のケースは今回の3カテゴリ(健診日程/受診・再検査報告/ +診断書提出)とは別に、専用のトークフローを別途用意する想定(設計は今後確定)。 + +**最終確定方式(本ファイル末尾「最終設計確定」節参照)**: 固定メニュー(persistentmenu、 +「健康診断について」1ボタンのみ)で常時アクセス可能な入口を作り、タップ後はbutton_template +(メッセージ内埋め込みボタン)で4択を提示するハイブリッド方式。当初は固定メニュー4ボタン +単独で設計していたが、PC版での表示方式(三本線展開が必要)が期待と異なっていたため転換した +経緯が「背景・設計転換」以下に残っている(読み進める場合は時系列の検討過程として参照)。 + +**情報源の役割分担(確定・2026-09-06)**: `健康診断管理/プリザンターテーブル設計書_健康診断.xlsx` +「メッセージ設定」シートは管理者が見る・編集するメッセージ本文の一覧(一次情報源)、 +本ファイル(design-fixed-menu.md)は技術的な実装設計(トリガー条件・データ構造・ +n8n実装方針等)を扱う。メッセージ本文を変更する場合はExcelを直接編集し、本ファイルは +それを参照する形にする。 + +**ExcelとPleasanter Process定義`Description`欄(Bot案内文の実体)の同期運用 → 確定 +(2026-09-07)**: 開発時はClaudeがExcel「メッセージ設定」シートの内容をPleasanterの +各Process定義の`Description`欄へ転記する。運用開始後、メッセージ文言を変更する場合は +管理者がExcelとPleasanter両方を直接編集する(自動同期の仕組みは作らない)。 + +**健康診断管理システム全体のフェーズ構成(確定・2026-09-06)**: +- フェーズ1: LINEWORKSアンケートフォームの回答結果を508971(社員別健康診断管理)へ反映 + (未着手。本ファイルの設計完了後に設計・開発着手予定) +- **フェーズ2(本ファイルが対象)**: LINEWORKS Botを使った対話形式のワークフロー + (固定メニュー・button_template・Process連携。現在設計中) +- フェーズ3: 産業医が診断データを確認し就業判定可否を入力するための、Pleasanter外部UI。 + データはPleasanterに置いたまま連携する(まだ設計に入らない) + +フェーズ2はレコードが既に508971に存在する前提(Status=100以降)で設計している。 +フェーズ1未着手の間の検証は、既存レコードを壊さない捨てレコードを手動作成して行う +(7章「導入・検証方針」参照)。 + +**フェーズ3との整合性 → 確定(2026-09-06)**: フェーズ2の「就業判定_可/不可」 +「再検査通知」は管理者/産業医が`Class052`(健診結果)・`Class053`(就業判定)を見て +手動でProcessボタンを押す設計だが、将来フェーズ3(産業医UI)が稼働してもこの手動判断 +フロー自体は不要にならない。フェーズ3は`Class052`/`Class053`への**入力元**が +Pleasanter外部UIに変わるだけで、その後の「値を見てProcessボタンを押す」という +フェーズ2側の設計は影響を受けない。 + +## 背景・設計転換 + +当初のHC-WA/HC-SUB設計は「現在Statusに紐づくProcess一覧をbutton_templateで動的提示し、 +ユーザーがラベルで選ぶ」方式だった。固定メニュー(Bot Persistent Menu API、 +`POST /v1.0/bots/{botId}/persistentmenu`)はトーク画面下に常時表示される最大4ボタンの +固定入口であるため、この方式に切り替える。 + +- ボタンは最大4つ(LINEWORKS仕様上の制約) → 4機能の要件とちょうど一致 +- 固定メニューは**1対1(個人)トークのみ有効**。Botが入ったグループトークでは表示されない + (複数Bot参加トークルームでは全Bot共通でメニュー機能が使えない、という制限もあるが、 + 健康診断Botは個人対話前提のためグループ運用は考慮不要) +- ボタン(`type:"message"`)タップ時は通常のテキストメッセージと同じ形でBot Webhookに届く + (`postback`の値がテキストとして送られる想定) +- **表示方式は実機確認済み(2026-09-06、PC版・スマホ版LINEWORKS両方)**: + - PC版: 常時表示のボタン列ではなく、入力欄右下の三本線(≡)アイコンをタップすると + 4項目がポップアップメニューとして展開される方式 + - **スマホ版(Android実機確認済み): 当初の想定通り、入力欄のすぐ下に4ボタンが常時表示の + ボタン列として並ぶ**。「トーク内にクリックするだけのボタンが出る」という期待に完全一致 + - ラベル(健診日程/受診・再検査報告/診断書提出/ストレスチェック)は両方の表示形式で + 正しく反映されることを確認済み。診断書提出フローの対話中(Botからの確認メッセージ後)でも + アクセスできることを確認済みのため、「対話フロー中にメニューへアクセスできるか」という懸念は解消 + - **リッチメニュー(Rich Menu)との比較 → 固定メニューのまま進める(2026-09-07確定)**: + LINEWORKS公式ドキュメント(`bot-menu`)によると、リッチメニューもPC/ブラウザ版では + 固定メニューと同様にドロップダウン展開になる仕様であり、PC版の表示方式はどちらを + 使っても変わらない。モバイル版は固定メニューで既に「常時表示ボタン列」というUXを + 達成できているため、画像作成・アップロードの手間が発生するリッチメニューへの切替は + 不採用 + +この転換により、HC-SUBの役割は「4カテゴリいずれかの対話完了時にProcess実行+固定文言の +結果案内」に単純化される。HC-WAの動的選択肢マッチング(pendingProcesses)は不要になり、 +固定メニューのpostback(カテゴリ名)を受けて現在Statusから対応アクションを解決する +分岐ロジックが中心になる。 + +## Status遷移全体像(Excel雛形14項目 × 今回の役割分担) + +| Status遷移 | 実行者 | Bot関与 | +|---|---|---| +| 100→200(アンケート発行) | 管理者操作 | なし | +| 210→250(申込中) | 管理者操作 | なし | +| 250→260(検査機関確定、2026-09-07追加) | 管理者が病院・日程等の必要情報を入力して保存すると自動遷移 | なし | +| 260→300(日程通知済み) | 管理者がProcess実行(現状は手動。将来n8n側で自動化する可能性あり、未確定) | HC-WP経由でBot通知(候補日案内) | +| 300→310(日程確定) | 本人がBot対話 | HC-WA固定メニュー「健診日程」 | +| 310→400(受診済) | 本人がBot対話 | HC-WA固定メニュー「受診/再検査報告」 | +| 400→410(診断書保存) | 本人がBot対話 | HC-WA固定メニュー「診断書提出」 | +| 410→500(就業判定済み) | 管理者/産業医操作 | なし | +| 500→800(再検査通知) | 管理者操作 | HC-WP経由でBot通知(再検査案内) | +| 800→810(再検査受診済み) | 本人がBot対話 | HC-WA固定メニュー「受診/再検査報告」 | +| 500→900・810→900(完了) | 管理者操作 | なし | +| 任意→910/920/930(対象外/保留/その他) | 管理者操作 | なし | + +「日程通知」「再検査通知」の2箇所はHC-WP(担当者操作起点のStatusプッシュ通知)の用途として +確定。既存設計のHC-WPはそのまま活きる。 + +**Status列の直接編集は不可にする(2026-09-07確定)**: 508971画面上でStatus列を管理者が +手動で書き換えられてしまうと、Bot対話中の待機状態(`bot_conversation_state`)とレコードの +実際のStatusがずれる懸念があったため、Status列はProcess経由でのみ変更できるよう編集不可に +する(EditorColumnsから除外する等)。これにより「Bot対話中に管理者がStatusを直接変更する」 +という競合ケース自体が発生しなくなる。なお`Date001`(候補日)は管理者が入力する項目のまま +変更可能で、Bot対話中に変わっても実害はない(対話中の承諾/変更は`Date002`確定日に書き込む +別項目のため)。 + +**Status=250/260等での長期停滞レコードの検知・催促 → 保留(2026-09-07)**: 自分手配を含め、 +今回は未確定のまま保留とする(HC-REMINDERへの追加は今後の検討課題)。 + +## Bot対話用Process定義(508971へ新規追加、固定メニュー経由) + +| Name | CurrentStatus→ChangedStatus | ValidateInputs | +|---|---|---| +| 日程_確定 | 300→310 | Date002(確定日、必須) | +| 報告_受診済み | 310→400 | なし | +| 報告_再検査受診済み | 800→810 | なし | +| 診断書_保存 | 400→410 | AttachmentsA(必須) | + +命名規則: Process.Name(内部名、DisplayNameとは別)に「カテゴリ_内容」の +プレフィックスを付与し、固定メニューのpostback→カテゴリ名への変換テーブルを介して +現在StatusのProcesses一覧からプレフィックス一致するものを検索・実行する解決方式にする。 +Name/DisplayNameは自由文字列(既存実例`実行予算WF申請`サイトのprocesses.jsonで確認済み)。 + +### 3択button_templateのpostback値(確定・2026-09-06、内部コード方式から変更) + +**旧設計(内部コード`MENU_SCHEDULE`等、下記は不採用・履歴として残す)**: postbackを +英数字の内部コードにし、HC-WA側で変換テーブルを介してカテゴリ名へ変換する方式だった。 +button_template方式への転換に伴い、旧Express実装や固定メニュー本体(「健康診断について」)と +表記を統一するため、**ラベルと同じ日本語テキストをそのままpostbackにする方式に変更** +(button_templateは直前のBotメッセージという文脈に紐づく選択肢のため、固定メニューの +常時入口方式より手入力テキストとの誤衝突リスクが低いと判断)。 + +| ボタン表示ラベル(=postbackのtext) | カテゴリ名(内部) | Processプレフィックス | +|---|---|---| +| 健診日程 | 日程 | `日程_` | +| 受診・再検査報告 | 報告 | `報告_` | +| 診断書提出 | 診断書 | `診断書_` | + +button_template送信時のボタン定義(`type:"message"`、`label`と`text`を同一にする): + +```json +{ + "content": { + "type": "button_template", + "contentText": "ご用件を選んでください。", + "actions": [ + { "type": "message", "label": "健診日程", "text": "健診日程" }, + { "type": "message", "label": "受診・再検査報告", "text": "受診・再検査報告" }, + { "type": "message", "label": "診断書提出", "text": "診断書提出" } + ] + } +} +``` + +このbutton_templateは、固定メニュー「健康診断について」タップ(テキスト受信)を +トリガーにHC-WAが返信する(「最終設計確定」節参照)。 + +### 表示メニューの絞り込み(確定・2026-09-06) + +3択は常に固定で出さず、**現在のStatusに合致しないカテゴリはそもそもactionsに含めない** +(ボタンを押させてから「現在対象外です」と弾く方式は不採用)。button_template組み立て時に +508971の対象レコードの現在Statusを見て、以下の対応表で該当するカテゴリだけをactions配列に含める。 + +| カテゴリ | 対象Status | +|---|---| +| 健診日程 | 300(日程通知済み)/310(日程確定済み、確定日照会のみ) | +| 受診・再検査報告 | 310(受診報告待ち)/800(再検査報告待ち) | +| 診断書提出 | 400(受診済、診断書提出待ち) | + +該当するカテゴリが1つもない場合(Status=100〜250、410〜510、900〜930等)は、 +「現在ご案内できる項目はありません」とだけ返信し、button_template自体を送らない。 + +この絞り込みにより、旧「3カテゴリの対話フロー詳細」各節末尾の「それ以外のStatus: +『現在対象外です』」という分岐は、実運用では基本的に到達しない防御的なフォールバックとして +残る(固定メニュー経由ではなく直接テキスト送信されたイレギュラー入力への保険)。 + +## 3カテゴリの対話フロー詳細 + +### 1. 健診日程(`日程_*`) + +- Status=300(日程通知済み)時: button_templateで案内(確定・2026-09-06、旧Express実装 + `sendDateShortcutButtons`のショートカットボタン方式を踏襲。LINEWORKS Bot APIには + 日付選択UI/datetimepickerアクションが存在しないため、ネイティブピッカーではなく + ボタン+自由入力のハイブリッドで対応): + ```json + { + "type": "button_template", + "contentText": "候補日は{Date001}です。この日程でよろしければ「承諾」、変更したい場合は候補から選ぶか日付を直接入力してください。", + "actions": [ + { "type": "message", "label": "承諾", "text": "承諾" }, + { "type": "message", "label": "来週も同じ曜日", "text": "{Date001+7日}" }, + { "type": "message", "label": "再来週も同じ曜日", "text": "{Date001+14日}" } + ] + } + ``` + - 承諾ワード(「承諾」「OK」等)受信 → Date001の値をそのままDate002へコピーして + Process`日程_確定`実行 + - ショートカットボタン(`Date001+7日`/`+14日`の具体的な日付テキスト)受信 → + その日付をDate002にセットしてProcess`日程_確定`実行 + - それ以外の自由入力テキスト受信 → 旧Express実装の和暦・月日省略対応パースロジックを + 流用して日付として解釈を試みる → **パース成功時は即確定せず、「{パース結果の日付}で + よろしいですか?(はい/いいえ)」と確認を挟む**(自由入力の誤爆防止、`awaitInput`を + `yesno_confirm`系の確認状態へ遷移) → 「はい」でDate002にセットしてProcess実行、 + 「いいえ」なら日程入力からやり直し。パース失敗時は「日付を認識できませんでした。 + 別の書き方でお試しください」と再案内。**パース失敗が3回連続したら強制中断 + (2026-09-07確定、診断書提出のPDF判定失敗時と同じ仕様)**: 待機解除し「日付を + 認識できないため、いったん受付を中断します。固定メニューから「健診日程」を + 選び直してください」と案内(失敗回数カウンタは`bot_conversation_state`で管理。 + 選び直したタイミングでカウンタはリセット) + - キャンセルワード → 待機解除 +- Status=310(日程確定)時: 確定日(Date002)を表示するのみ(Process不要、照会のみ) +- それ以外のStatus: 「現在対象外です」 + +### 2. 受診/再検査報告(`報告_*`) + +Status=310(受診報告)とStatus=800(再検査報告)で質問形式が異なる(2026-09-06確定)。 + +**Status=310時**: 単純な受診確認のみ +- ボタン押下 → 「健康診断の受診は完了しましたか?(はい/いいえ)」 +- 「はい」 → Process`報告_受診済み`実行(310→400) +- 「いいえ」/キャンセル → 待機解除 + +**Status=800時**: 再検査結果の4項目をトークで1問ずつ収集(確定・2026-09-06)。 +各項目、回答受信のたびに確認ステップを挟む(2026-09-06確定、脱線・誤入力対策): +- ボタン押下 → 「再検査の結果について、以下を順番にお伺いします。」に続けて + 1問目「a. 再検査受診日を教えてください」を送信 → `awaitInput:"report_form"`, + `awaitColumn:"a"`で待機 +- a回答受信 → 「a. 再検査受診日:{回答}でよろしいですか?(はい/いいえ)」と確認 + (`awaitInput`は`report_form`のまま、確認中フラグを内部的に保持) → + 「はい」なら一時保存して次の質問(b)へ進む。「いいえ」ならもう一度aを聞き直す + (確認せず訂正入力を直接受け付ける) +- 同様にb「受診医師名」→確認→c「検査項目、結果(概要)」→確認→d「就労の可否」→確認、 + の順で1項目ずつ確認を挟みながら進める +- d確認完了 → 4項目揃ったので、`a〜d`をまとめたテキスト(後述フォーマット)を + 508971の`DescriptionG`(再検査報告)へ保存 → Process`報告_再検査受診済み`実行 + (800→810)→ 「再検査の報告を受け付けました。ご対応ありがとうございました。」 +- 各ステップでキャンセルワード受信 → 一時保存していた回答も破棄し待機解除 + (4項目のうち一部だけ回答済みでも保存しない。やり直す場合は「受診・再検査報告」を + 選び直せば最初(a)から) + +`DescriptionG`への保存フォーマット(プレーンテキスト、4項目をまとめて1つの欄に): +``` +a.再検査受診日:{aの回答} +b.受診医師名:{bの回答} +c.検査項目、結果(概要):{cの回答} +d.就労の可否:{dの回答} +``` + +対象Status(310・800)以外: 「現在対象外です」(ただし「表示メニューの絞り込み」節参照。 +そもそもボタン自体を出さない設計に変更したため、この分岐は主に固定メニュー経由の +直接テキスト送信等イレギュラー入力への保険として残る) + +### 3. 診断書提出(`診断書_*`) + +- Status=400時のみ有効。ボタン押下 → 「診断書をPDFでスキャンしてアップロードして + ください。写真は不可です。表裏が別ファイルの場合は複数枚送信できます。 + すべて送り終わったら「完了」と送信してください。」 +- ファイル受信のたびに次を実施: + 1. PDF判定(後述「PDF判定方式」参照)。PDF以外 → 「診断結果をお送りいただきましたが、 + 画像での送信でしたので、PDFで再度お送り頂きますようおねがいいたします。」と再案内、 + 待機継続(受信済みファイルは破棄せず保持)。**PDF以外の受信が3回連続したら強制中断 + (2026-09-06確定)**: 待機解除し「PDF形式での送信が確認できないため、いったん + 受付を中断します。固定メニューから「診断書提出」を選び直してください」と案内 + (`bot_conversation_state`に失敗回数カウンタが必要、`report_form`の一時保存と + 同様の仕組みで管理)。**固定メニューから「診断書提出」を選び直したタイミングで + 失敗カウンタ・受信済みファイル一覧ともにリセットされる(2026-09-06確定)** + 2. PDF確認OK → ダウンロード→Pleasanter添付アップロード(Attachmentsへ追加、 + Process実行はまだしない)→「1件受け付けました。続けてファイルがあれば + 送信、なければ「完了」と送信してください」と返す +- 「完了」受信 → アップロード済み件数が1件以上ならProcess`診断書_保存`実行 + (400→410)。0件(ファイル未送信のまま「完了」受信)なら + 「まだファイルが送信されていません」と返し待機継続 +- キャンセル → 待機解除(アップロード済みの一時分は破棄しない。Pleasanter側は + Process未実行のためStatusは400のまま。再度「診断書提出」を選べば続きから + やり直せる) +- それ以外のStatus: 「現在対象外です」 + +**10分タイムアウトによる自動確定(2026-09-07確定)**: PDFを1枚以上アップロード済みの状態で +「完了」を送らないまま、**最後にPDFを受信した時刻から10分経過**したら、Process`診断書_保存` +を自動実行する(400→410)。自動確定時もユーザーへは通常の完了メッセージを送る(本人が +「完了」を送った場合と同じ案内)。PDFを1枚も送らずに10分経過した場合(ボタン押下のみで +離脱)はProcess実行せず待機解除のみ。監視は10分間隔の新規Scheduleワークフローで行う +(既存方針「タイムアウト監視ワークフローは持たない」の例外、診断書提出フロー専用)。 + +離脱→選び直しでPleasanter側にアップロード済みファイルが混在するケース(1回目に1枚 +アップロード後に離脱、2回目に選び直してさらに送信→「完了」、のような流れで前回分と +今回分が両方Attachmentsに残る)は**無視してよい(2026-09-07確定)**。内容に問題があれば +管理者が確認後、再提出フローへ差し戻す運用でカバーする。 + +#### PDF判定方式 + +LINEWORKS Botコールバックの`content`にはfileIdのみが含まれ、MIME種別・ +ファイル名は含まれない(公式ドキュメント`bot-callback-message`で確認済み)。 +コンテンツダウンロードAPI(`GET /v1.0/bots/{botId}/attachments/{fileId}`)も +302リダイレクト後の最終レスポンスヘッダー仕様が公式未記載のため、以下の +二段判定で実装する(実機確認しだい調整可): + +1. ダウンロード後のレスポンスヘッダー`Content-Type`が`application/pdf`なら確定 +2. 上記が取得できない/一致しない場合、ファイル本体の先頭バイトを見て + マジックナンバー`%PDF`(0x25 0x50 0x44 0x46)で判定するフォールバックを入れる + +### キャンセル共通仕様 + +どのawaitInput状態でも、テキスト入力の先頭で「キャンセル」「やめる」「戻る」「中止」 +「取消」「cancel」「stop」(大文字小文字区別なし)のいずれかのワード一致を最優先チェックし +(2026-09-06、「中止」「取消」および英語表記を追加確定)、一致したら待機状態をクリアして +「メニューからやり直してください」と返す。 + +### 担当者への連絡導線(2026-09-07追加) + +自由入力での意図判別が難しいテキストベースの相談要望(日程調整の個別事情等)に対応するため、 +選択肢(固定メニュー・button_template)に「担当者に連絡」を常設し、選ばれたら固定の案内 +メッセージを返す方式で対応する。 + +**構成**: +1. 固定メニュー(persistentmenu)を「健康診断について」「担当者に連絡」の2ボタン構成に + 変更する +2. button_templateで選択肢を提示する全箇所の末尾に、共通で「担当者に連絡」ボタンを追加する + (対象3箇所、いずれも最大10ボタンの制限に対し余裕あり。LINEWORKS公式ドキュメント + `bot-send-button`で最大10ボタンと確認済み): + - 「健康診断について」タップ後の3択(健診日程/受診・再検査報告/診断書提出)→4択に + - 「健診日程」の候補日提示(承諾/来週も同じ曜日/再来週も同じ曜日)→4択に + - 「受診・再検査報告」(Status=310時)の受診確認(はい/いいえ)→3択に +3. 「担当者に連絡」受信時の挙動は、**経由した入口によって分ける(2026-09-07確定)**: + - **button_template内の選択肢として選ばれた場合**(4択・3択の一部)→ それまでの対話の + 待機状態(`awaitInput`)はリセットせず維持したまま、案内メッセージだけ追加で返す + (今の対話の一部としての相談、元のフローはそのまま続けられる) + - **固定メニューから直接選ばれた場合** → 待機状態(`awaitInput`)もリセットしてから + 案内メッセージを返す(対話を中断してでも相談したい、という利用シーンのため) +4. 固定メニューの「担当者に連絡」は、対象者解決(504412⇔508971照合)を含め、 + 「健康診断について」と同じ処理フローを通す(特別扱いしない、2026-09-07確定)。 + 対象レコード0件(新入社員等)の場合は既存ルール通り「対象の健診情報がありません」が + 返る +5. 対象外: 診断書提出(ファイル受信待ち)・受診/再検査報告のStatus=800時(a〜d自由記述 + 収集)は、選択肢を提示しないテキスト/ファイル入力中心のフローのため今回のスコープ外 + (button_templateへの追加なし)。これらのフロー中でも固定メニューの「担当者に連絡」は + 常時反応する(4番の通り待機状態もリセットされる) + +**案内メッセージ文言(確定・2026-09-07、担当者窓口は「総務総合窓口」)**: +``` +ご用の方は総務総合窓口へお問合せください🙇 +https://line.worksmobile.com/message/send?userNoList=800300000001350 +``` + +## awaitInput状態の拡張 + +既存(`none`/`date`/`file`)から以下へ拡張: + +- `schedule_confirm`: 日程承諾/ショートカットボタン選択/自由入力日付のパース/キャンセルの + 分岐(健診日程用)。自由入力の場合はパース後さらに`yesno_confirm`(「この日付で + よろしいですか?」)へ遷移してから確定する2段階(誤爆防止、2026-09-06確定) +- `yesno_confirm`: はい/いいえ/キャンセルの3方向分岐(受診報告で使用) +- `file`: 既存を拡張し、PDF形式チェックを追加(診断書提出用) +- `report_form`: 再検査報告(4項目)の順次収集用(受診・再検査報告のStatus=800時のみ)。 + `awaitColumn`を`"a"|"b"|"c"|"d"`として現在どの項目を聞いているかを保持する。 + a〜dの回答内容そのものは、既存の`bot_conversation_state`の列だけでは保持できないため、 + **一時保存用の新規列(例: `formDraft`、JSON文字列 `{"a":"...","b":"..."}`)を + `bot_conversation_state`データテーブルに追加する必要がある**(未実装、要追加)。 + 4項目揃った時点でDescriptionGへまとめて書き込み、この一時列はクリアする + +## 課題1(診断書ファイル添付処理)との関係 + +診断書提出フローの「PDF受信→ダウンロード→Pleasanterアップロード→Process実行」部分は、 +既存の未実装課題(`NodeSrv/apps/healthcheck-survey-bot/workflows/hc-wa-lineworks-response.json` +のTODOコメント参照)の実装がそのまま必要になる。設計は別途確定済み: + +- Pleasanter添付API: `POST api/items/{id}/update`、 + `AttachmentsHash:{AttachmentsA:[{ContentType,Name,Base64}]}` +- LINEWORKS添付ダウンロード: `GET https://www.worksapis.com/v1.0/bots/{botId}/attachments/{fileId}` + (Bearer token、302→Locationへ再GET) +- ファイル名規則: `{年度エリア(ClassZ)}_診断書_{名前(ClassC)}.{拡張子}` +- ノード構成変更案: 既存「IF: action==send」を「IF: action==error」に役割変更し、 + false側をsend/download_file共通の経路にする(詳細は本ファイル末尾の変更履歴、または + 会話ログ参照。実装時に`workflows-status.md`へ反映すること) + +## Status別カウンタ項目(Num031~046)設計・反映済み(2026-09-06追記) + +固定メニュー設計を進める過程で、508971のNum031~038(旧: 全社員人数/対象人数/受診済み/ +再検査/就業判定-可/就業判定-不可/SC受診済み/健診終了)が後付けでStatus15段階に対応しきれて +いないことが判明。508971・513156双方のNum031~046を、Status全段階に1:1対応する +累積型カウンタとして再設計・反映済み。 + +### 前提となるStatus定義変更 + +ユーザーがPleasanter画面上でStatus定義自体を変更し、500(就業判定済み)を +「500,就業判定_可」「510,就業判定_不可」の2つに分割(Class053の値による条件分岐が +不要になり、Process定義がシンプルになった)。Status全16段階に確定: + +``` +100,新規登録 / 200,アンケート発行 / 210,アンケート回答 / 250,申込中 / +260,検査機関確定 / 300,日程通知済み / 310,日程確定 / 400,受診済 / 410,診断書保存 / +500,就業判定_可 / 510,就業判定_不可 / 800,再検査通知 / 810,再検査受診済み / +900,完了 / 910,対象外 / 920,保留 / 930,その他 +``` + +**260「検査機関確定」追加の経緯(2026-09-07確定)**: 検査機関・候補日・コース等、日程通知に +必要な情報を管理者が入力する作業と、実際にBotへ通知を送る作業を分離するために新設。 +Pleasanterのプロセス機能には「実行種別」設定があり、通常の「追加したボタン」(手動クリック)の +ほかに「追加したボタン/作成・更新」を選ぶと、ボタンとして表示されつつ、標準の保存操作 +(アクション種別=「保存」)をトリガーにプロセスが自動実行もされる(250→260用のProcessは +この自動実行方式を使う。JSON上は`ExecutionType:10`、2026-09-07に実機画面で名称確認済み。 +本ファイル内で単に「作成または更新」と書いている箇所は同じ「追加したボタン/作成・更新」を +指す)。260→300(Bot通知)は従来通り担当者がボタンを押す手動実行のまま(将来的にn8n側の +別ワークフローで自動化する可能性があるが未確定)。 + +**「追加したボタン/作成・更新」実行種別の発動条件 → 確定(2026-09-07、ユーザーが実機の設定画面で確認)**: +「現在の状況」(CurrentStatus)がレコードの実際のStatusと一致し、かつ「条件」タブの +フィルタ条件に合致した場合のみプロセスが処理される(両方を満たさないと発動しない)。 +250→260のProcessをCurrentStatus=250(申込中)で作成しておけば、Status=260到達後に +同レコードを再度編集・保存してもCurrentStatus不一致のため再発火しない。 +**一方、「入力検証」タブ(ValidateInputs)はこの実行種別では処理されない**(適用されるのは +「追加したボタン」実行種別のみ)。そのため`Class003`・`Date001`・`Class004`の必須化は +ValidateInputsではなく「条件」タブのフィルタ条件で表現する必要がある(旧記述の +「ValidateInputsが自動実行の発火条件として機能する」は誤り、下表の想定Process一覧を訂正)。 + +### Num031~046対応表(確定・反映済み) + +| Num | 項目 | Status | 初期値 | +|---|---|---|---| +| 031 | 新規登録人数 | 100 | **1**(DefaultInput、新規作成時に自動セット) | +| 032 | アンケート発行 | 200 | 0 | +| 033 | アンケート回答 | 210 | 0 | +| 034 | 申込中 | 250 | 0 | +| 047 | 検査機関確定 | 260 | 0(2026-09-07追加、Num047は空き列で確認済み) | +| 035 | 日程通知済み | 300 | 0 | +| 036 | 日程確定 | 310 | 0 | +| 037 | 受診済み | 400 | 0 | +| 038 | 診断書保存 | 410 | 0 | +| 039 | 就業判定_可 | 500 | 0 | +| 040 | 就業判定_不可 | 510 | 0 | +| 041 | 再検査通知 | 800 | 0 | +| 042 | 再検査受診済み | 810 | 0 | +| 043 | 完了 | 900 | 0 | +| 044 | 対象外 | 910 | 0 | +| 045 | 保留 | 920 | 0 | +| 046 | その他 | 930 | 0 | + +**集計方式は累積型(以上)**: 該当Statusへ遷移した時点でProcessのDataChanges +(Type:"InputValue")により該当Num列を1にセットする。一度1になったら0に戻さない +(例: 「受診済み」は400以降のどのStatusに進んでもずっと1のまま)。 + +**「全社員人数」「対象人数」相当の項目は廃止**。全体件数は513156側でレコード件数 +(Count集計)を使えば代用できるため、専用のNum列を持たない方針に確定。 +SC受診済み(旧Num037相当)も今回のスコープから削除。CheckB自体は508971に残るが、 +Status対応表には含めない(固定メニュー「ストレスチェック」機能は今回の対象範囲外、 +2026-09-07確定。冒頭の注記参照)。 + +### 反映済み内容(2026-09-06、本番) + +- 508971: Num031~038のLabelText置換+DefaultInput設定、Num039~046新規追加。 + EditorColumnHash._Tab-2(カウンタータブ)末尾へNum039~046を追加配置。 + Style Id2(数値エリア調整)の対象セレクタへNum039~046を追加 + (スクリプト: `.claude/js/site-scripts/健康診断管理/redefine-num031-046-508971.js`) +- 513156: 508971と同一のLabelText構成に統一。EditorColumnHash.General + (Num038の直後)へNum039~046を追加配置。Style Id2も同様に追加 + (スクリプト: `redefine-num031-046-513156.js`) +- 508971のSummaries(513156への集計設定)へNum039~046の8件を追加(Id31~38)。 + 既存Num031~038分(Id1~8、508971→513156、Type:Total、LinkColumn:ClassZ)は + 列の意味が変わっただけで設定自体は流用(スクリプト: + `add-summaries-num039-046-508971.js`)。Summaries合計42件(Num031~046の16件+ + Num061~073/081~093の診断結果26件、2026-09-07時点。検査項目分割の経緯は次節参照) + +### 検査項目「聴力検査」の3分割(2026-09-07追加) + +Num061のLabelText「力検査」はデータ不備による表記(先頭の「聴」が欠落)と判明。 +実態は労働安全衛生規則の健診項目「聴力検査(その他の方法による検査を含む)」で、 +実運用では聴力検査を1000Hz/4000Hz/その他(オージオメータ以外の方法)の3項目に +分けて記録する必要があることが確認できたため、検査項目別カウンタを11項目から +13項目へ拡張した。 + +- 508971・513156ともにNum061~071(実施人数)→Num061~073、Num081~091(有所見者数) + →Num081~093へ拡張(各11列→13列、新規4列はNum072/073/092/093) +- 項目順(実施・有所見共通13項目): 聴力検査(1000Hz)/聴力検査(4000Hz)/ + 聴力検査(その他)/胸部エックス線検査/喀痰検査/血圧/貧血検査/肝機能検査/ + 血中脂質検査/血糖検査/尿検査(糖)/尿検査(蛋白/たん)/心電図検査 +- 「聴力検査(その他)」にのみDescription「その他の方法による検査」を設定(旧Num061の + Description流用)。1000Hz/4000Hz側はDescriptionなし +- 508971のEditorColumnHash._Tab-1・513156のEditorColumnHash.General・両サイトの + Styles Id2(数値エリア調整セレクタ)・508971→513156のSummaries(新規4件、 + Num072/073/092/093、Type:Total、LinkColumn:ClassZ)を合わせて更新済み +- 実施済みレコード13件はいずれもNum061~091が未入力(0)のみだったため、値の移行は + 不要(ラベル・列追加のみで対応完了)。スクリプト: + `.claude/js/site-scripts/健康診断管理/restructure-hearing-test-508971-513156.js` +- 508971・513156のNum031~046 LabelTextが完全一致することを確認済み + +### 実施済み(2026-09-07、Process定義17件を本番反映) + +508971のProcesses定義17件全件(検査機関確定含む)をAPI経由で作成済み。 + +- Id1~3,5~17(13件): スクリプト`add-processes-batch1-508971.js`(Status遷移+OnClickのみの + 単純グループ12件)・`add-processes-batch2-508971.js`(ValidateInputs・Date/Check系 + DataChangesを含む4件:日程通知済み・日程_確定・診断書_保存・再検査通知)。Date/Check系 + DataChangesは社内実例で裏付けが取れた形式(`InputDate`/`InputValue`)をそのまま使用 +- Id4「検査機関確定」(250→260): スクリプト`add-process-260-508971.js`。実行種別 + 「作成または更新」=`ExecutionType:10`(社内189112実例で23件全一致、裏付け確定)。 + 条件タブ(`View.ColumnFilterHash`)は、否定フィルタUIがProcessの条件タブには + 存在しないと実機確認できたため、`Class003`(検査機関)・`Class004`(コース)は + 実行時点の実際の選択肢ResultIdを配列で列挙する方式、`Date001`(候補日)は公式マニュアル + (`api-view`)の日付範囲指定を使い広い範囲(1900/01/01〜2999/12/31)を指定して + 「入力されていれば必ずヒットする」条件にした + +**Num列へのDataChanges(各Processに`Num0XX=1`をセットする部分)は全17件に反映済み +(2026-09-07)**: 「アンケート発行」(Id1)にユーザーが実機で`Num032=1`を試験追加した +形式と同じ(`InputValue`、`ValueFormulaNotUseDisplayName`/`ValueFormulaIsDisplayError`は +false)で、残り16件(Id2~17)へAPI経由で展開済み(スクリプト: +`add-num-datachanges-remaining-508971.js`)。**実際にProcess実行してNum列が1になるかの +動作確認はこれから**(ユーザーが後日まとめて確認予定)。 + +**Processesの並び順を修正(2026-09-07)**: batch1→batch2→260の投入順のまま追加していた +ためPleasanter管理画面のプロセス一覧がId順になっておらず分かりにくいと指摘を受け、 +Id昇順(Status遷移の自然な流れ、1アンケート発行〜17その他)に並べ替えた +(スクリプト: `reorder-processes-508971.js`、各Processの中身は変更なし)。 + +**不具合修正: Status列に260(検査機関確定)が未登録だった(2026-09-07発見・修正)**: +500/510分割時のStatus定義変更(前節「前提となるStatus定義変更」参照)に260を含め +忘れており、`Status`列の`ChoicesText`に260のエントリが存在しなかった。このため +「検査機関確定」Processの「変更後の状況」欄が画面上で名前解決できず`?260`と表示される +不具合が発生(ユーザーが実機で発見)。250(申込中)と300(日程通知済み)の間に +`260,検査機関確定,機関確定,new-status-purple`を追加して解消(スクリプト: +`add-status-260-508971.js`)。513156(実施年度-集計)にはStatus列自体が存在しないため +対応不要。 + +**検査機関確定Processの発火条件を選択肢列挙方式からCheck列方式へ変更(2026-09-07確定・ +実装済み)**: 実機でClass003/Class004の条件タブを確認したところ、選択肢を配列で +静的に列挙する方式では、511924/513284へ新しい選択肢が追加されるたびに条件タブの +更新が必要になり、メンテ漏れリスクがあると判明。そもそもClass003・Class004は単一選択 +項目のため、値の有無だけを判定できれば十分という前提のもと、専用Check列 +`CheckC`(ラベル「日程情報入力済」、EditorColumnHash.GeneralのDate002直後に配置)を +新設し、ServerScriptで自動セットする方式に変更した。 + +- ServerScript「検査機関確定準備完了同期スクリプト.js」(`BeforeCreate`/`BeforeUpdate` + 両方でtrue、Id0): `Class003`・`Date001`・`Class004`が全部値ありなら`CheckC`をtrueに + セットする。構造は社内実例(新版営業積算システム504322「状況Class062自動同期 + スクリプト」)の`{Title,Name,BeforeCreate,BeforeUpdate,Body,Id}`形式で裏付け済み +- Process「検査機関確定」(Id4)の`View.ColumnFilterHash`を`{"Class003":[...],"Date001":[...], + "Class004":[...]}`(選択肢列挙)から`{"CheckC":"true"}`(単純な条件)へ置き換え +- これにより511924・513284へ新しい選択肢を追加してもProcess側のメンテは一切不要になった +- スクリプト: `.claude/js/site-scripts/健康診断管理/add-checkc-and-fix-process260-508971.js` + +~~**運用上の注意**: `Class003`・`Class004`は選択肢を配列で静的に列挙しているため、 +511924「受診病院一覧」・513284「検診種別マスタ」へ新しい選択肢を追加するたびに、 +`検査機関確定`Processの`View.ColumnFilterHash`も更新しないと、新しい選択肢を選んだ +レコードだけ発火条件から漏れる。病院・コース追加時のチェックリストに +「検査機関確定Processの条件更新(`add-process-260-508971.js`の再実行)」を含めること。~~ +(2026-09-07、CheckC方式への変更により解消・撤回。以後この対応は不要) + +**HC-WPが送信するProcess2件のDescription欄へメッセージ本文を転記済み(2026-09-07)**: +Excel「メッセージ設定」シート①・⑤の本文をそのまま転記した(プレースホルダーは +`{検査機関}`等のラベル名形式のまま、HC-WP側で値に置換する設計)。 + +- Process「日程通知済み」(Id5): ①受診日のお知らせの本文 +- Process「再検査通知」(Id11): ⑤再検査通知の本文 +- スクリプト: `.claude/js/site-scripts/健康診断管理/set-description-508971.js` +- 他のProcess(②③④⑥⑦はStatus変化トリガーではなく日次リマインダー/ユーザー起点のため + Description欄は使わない。設計上Process経由でHC-WPが送るのは①⑤の2件のみ) + +### Process定義一覧(全17件、2026-09-07反映済み) + +Status遷移・DataChanges(Num列含む)・ValidateInputsを全件反映済み(Bot対話用4件+ +管理者操作用、詳細は本ファイル前半「Status遷移全体像」参照)。Num列DataChangesの +実機動作確認(Process実行して実際にNum列が1になるか)はユーザーが後日実施予定。 + +| Process名 | Status遷移 | DataChanges | ValidateInputs | +|---|---|---|---| +| アンケート発行 | 100→200 | Num032=1 | なし | +| アンケート回答 | 200→210 | Num033=1 | なし | +| 申込中 | 210→250 | Num034=1 | なし | +| 検査機関確定(2026-09-07追加) | 250→260(実行種別「作成または更新」で自動実行、CurrentStatus=250指定) | Num047=1 | 入力検証タブは不使用(この実行種別では処理されない)。**条件タブの対象列は「日程通知済み」Processと同一(`Class003`・`Date001`・`Class004`、2026-09-07確定)**。3列とも入力済みであることを発火条件にする | +| 日程通知済み | 260→300(HC-WP経由でBot通知)。**`Class003`=「自分手配」(ResultId 513480)の + レコードは対象外**(専用トークフロー未確定、2026-09-07追加) | Num035=1 | **`Class003`(検査機関)・`Date001` + (健康診断候補日)・`Class004`(コース)を必須化(確定・2026-09-06、①送信メッセージの + 差込変数が空欄のまま送信されるのを防ぐ)。`Class005`/`Class006`/`Class007`は任意 + (オプション自体が任意選択のため) | +| 日程_確定 | 300→310(Bot対話) | Num036=1 | Date002(確定日、必須) | +| 報告_受診済み | 310→400(Bot対話) | Num037=1 | なし | +| 診断書_保存 | 400→410(Bot対話) | Num038=1 | AttachmentsA(必須) | +| 就業判定_可 | 410→500 | Num039=1 | なし | +| 就業判定_不可 | 410→510 | Num040=1 | なし | +| 再検査通知 | 500→800(HC-WP経由でBot通知) | Num041=1、`Date003`(再検査通知日)に送信日をセット、 + `CheckA`(再検査連絡)をtrueに | なし(⑤本文は固定文言のみで差込変数なし) | +| 報告_再検査受診済み | 800→810(Bot対話) | Num042=1 | なし(`DescriptionG`への保存はBot対話側で実施、 + Process自体の入力検証は不要) | +| 完了(再検査不要) | 500→900 | Num043=1 | なし | +| 完了(再検査済み) | 810→900 | Num043=1 | なし | +| 対象外 | 任意→910 | Num044=1 | なし | +| 保留 | 任意→920 | Num045=1 | なし | +| その他 | 任意→930 | Num046=1 | なし | + +**DataChangesの裏付け状況(2026-09-07、社内の全production環境processes.jsonを横断調査)**: + +- 共通フィールド構造(実例確認済み): 各DataChangesは + `{"Id":n,"Type":"...","ColumnName":"...","Value":"...","ValueFormulaNotUseDisplayName":false,"ValueFormulaIsDisplayError":false}` + の形(`InputDateTime`のみ追加で`BaseDateTime`を持つ) +- **Check列 → 裏付けあり**: `{"Type":"InputValue","ColumnName":"CheckA","Value":"1"}`で + true相当にする実例が複数サイトに存在(488755等)。`CheckA`(再検査連絡)をtrueにする + DataChangesはこの形で実装してよい +- **Date列 → 裏付けあり**: 「今日」をセットするなら + `{"Type":"InputDateTime","ColumnName":"Date0XX","BaseDateTime":"CurrentTime","Value":"0,Days"}`、 + 「n日後」なら`{"Type":"InputDate","ColumnName":"Date0XX","Value":"n,Days"}`の実例が + 複数サイトに存在。`Date003`/`Date004`の送信日セットはこの形で実装してよい +- **Num列 → 裏付けなし(要注意)**: `{"Type":"InputValue","ColumnName":"Num0XX","Value":"1"}` + という組み合わせは、社内の全productionサイトのprocesses.jsonを横断検索しても実例が + 1件も見つからなかった。Class列に対する`InputValue`(`Value:"1"`等)の実例はあるため + 類推は成り立つが、Num列で同様に動作する確証はない。**Num031~047のカウンタ更新 + DataChangesは、実装時にユーザーが実機で1件だけ試験設定し、動作確認してから残りに + 展開すること**(前節「実装の役割分担」参照) + +### 【重大障害・原因判明】OnClickフィールドをAPI経由で指定すると個別レコード画面が +### 全件開けなくなる(2026-09-07発生・解決) + +API経由でProcess定義17件を作成した際、`OnClick`フィールドに`"$p.ex.processScript($(this));"` +(実行種別「追加したボタン」用の一般的な値、社内189112実例のコメントから引用したもの)を +明示的にセットしたところ、**当該Processを1件でも含めるだけで508971の個別レコード編集画面が +全件(対象レコードに関わらず)「指定された情報は見つかりませんでした」で開けなくなる障害が +発生した**。一覧画面(グリッド)・API(`get`/`update`)は正常に動作し続けたため発見が遅れた。 + +**原因**: ユーザーがPleasanter画面上で同じ17件を手動登録したところ、生成されたJSONには +**`OnClick`フィールドが一切含まれていなかった**(実行種別が「追加したボタン」でも +「追加したボタン/作成・更新」でも同様)。189112実例で見えた`OnClick`値は、別の生成経路 +(バージョン差・特殊な実装)による見た目上の値であり、汎用的にコピーしてよい値ではなかった。 + +**追加検証(2026-09-07、ユーザーが画面上でProcess1のOnClick欄に同じ値 +`$p.ex.processScript($(this));`を手動入力→保存)**: この場合は編集画面が壊れなかった +(Seleniumで513409の正常表示を確認)。**つまり`OnClick`の値自体が悪いのではなく、 +「API(`updatesite`)経由でOnClickフィールドを含むJSON全体を送信する」という経路自体に +問題があった**(画面上の正規UIフローで保存されたOnClick値は無害)。根本原因の内部的な +メカニズム(Pleasanter側のバリデーション・整形処理のどこで衝突しているか)は未解明の +ままだが、実運用上の対処(下記ルール)には影響しない。 + +**切り分け経緯**: ServerScript疑い→Process4単体疑い→Processes全体除外で復旧確認→ +バックアップから段階的に復元する二分探索で`Id2`(アンケート回答)単体でも再現確認→ +ユーザーが手動登録した17件と自分がAPI投入した17件のJSON構造を直接比較し、`OnClick`の +有無が唯一の違いと判明。切り分け作業はSelenium(`pleasanter-selenium-html-scrape` +スキル)で「指定された情報は見つかりませんでした」の再現有無を自動確認しながら進めた。 + +**さらに追加検証(2026-09-07、API経由)**: 「既存の(手動登録済み)Processes配列を +`getsite`で取得し、その`OnClick`未設定のプロパティへ`$p.ex.processScript($(this));`を +追記してそのまま`updatesite`で送信する」方式を、まずId2の1件→問題なければ残り15件へ一括、 +の順で試したところ、**いずれも編集画面は壊れなかった**(Selenium確認済み)。最終的に +17件全件へOnClickを付与した状態でも正常動作。 + +**最終結論**: 当初の障害の真因は「`OnClick`フィールドの存在」そのものではなく、 +**「Processオブジェクトをゼロから自分で組み立ててAPI送信した」ことによる、Pleasanter側の +生成物と比べた際の何らかの欠落・不整合**だったと考えられる(具体的にどのプロパティ/ +構造が欠けていたかは特定できていない)。「既存のPleasanter生成済みオブジェクトに +プロパティを追記するだけ」の更新であれば、`OnClick`を含め安全にAPI経由で操作できる。 + +**教訓・今後のルール**: +- **Process定義を新規にゼロから組み立ててAPI(`updatesite`)経由で送信するのは避ける** + (今回の障害の直接原因)。新規Processの追加は画面上の手動登録を基本とする +- **既存の(`getsite`で取得した)Processオブジェクトに対して、特定プロパティを + 追記・変更するだけの更新はAPI経由で問題なく行える**(`OnClick`を含む。DataChanges・ + Description・ValidateInputs・条件タブ・OnClick、いずれも2026-09-07に安全性を確認済み) +- 本番のProcess定義17件は、Process本体をユーザーが画面上で手動登録した後、 + DataChanges・Description・条件タブ・ValidateInputs・OnClickをすべてAPI経由の + 追記操作で完成させた(全項目、Selenium確認済み) + +**Process定義17件の最終状態(2026-09-07、全項目実装完了・Seleniumで編集画面の正常動作を +都度確認済み)**: + +- Process本体(Name/DisplayName/CurrentStatus/ChangedStatus/実行種別): ユーザーが + Pleasanter画面上で17件全件を手動登録 +- OnClick(`$p.ex.processScript($(this));`): 全17件へAPI経由(既存Processオブジェクトへの + 追記方式)で追加済み。Selenium確認済み、編集画面への影響なし +- 実行種別「追加したボタン/作成・更新」(検査機関確定=Id4)を画面で選ぶと、JSON上は + `ExecutionType:20`になると実機確認(2026-09-06に189112実例で確認した`ExecutionType:10` + とは異なる値。10と20の違いは未解明だが、20が「追加したボタン/作成・更新」に対応する + という事実だけで実装上は十分) +- Num列DataChanges全17件: API経由で追加済み(`OnClick`が存在しないことを事前チェックする + 安全策付きスクリプトで実施。1件(Id1)を試験→Selenium確認後、残り16件を一括展開) +- Id5(日程通知済み)・Id11(再検査通知)のDescription欄: API経由で追加済み +- Id4(検査機関確定)の条件タブ(`View.ColumnFilterHash`): API経由で`{"CheckC":"true"}`を + 追加済み +- Id5・Id6・Id8のValidateInputs: API経由で追加済み(Id5: + `Class003`/`Date001`/`Class004`必須、Id6: `Date002`必須、Id8: `AttachmentsA`必須) +- スクリプト一式: `.claude/js/site-scripts/健康診断管理/add-num-datachange-single-508971.js` + (単発試験用)・`add-num-datachanges-all-remaining-508971.js`(一括展開)・ + `add-description-508971.js`・`add-condition-process4-508971.js`・ + `add-validateinputs-508971.js`。いずれも対象Processの`OnClick`が`undefined`であることを + 確認してから処理する安全策を組み込み済み +- 動作確認方法: `.claude/skills/pleasanter-selenium-html-scrape`スキル(Selenium)で + 本番513409の個別編集画面を都度取得し、「指定された情報は見つかりませんでした」 + (`controller=errors`)が出ないこと・HTMLサイズが正常範囲(約20万文字)であることを + 確認する運用を今回確立した。同様の障害調査・確認作業でユーザーに手動確認を都度依頼する + 代わりに使える + +## 未確定・要検証事項(2026-09-06 調査・確定分) + +- **MIME種別/拡張子の取得方法 → 確定**: 公式ドキュメント`bot-callback-message` + でWebhookペイロード(`content`)にはfileIdのみが含まれ、fileName/mimeType等は + ないことを確認。コンテンツダウンロードAPI(`bot-attachment-get`)も302 + リダイレクト後の最終レスポンスヘッダー仕様は公式未記載。対応方針は上記 + 「PDF判定方式」節の二段判定(Content-Typeヘッダー→ダメならマジックナンバー + `%PDF`判定)に確定 +- **診断書の表裏提出方法 → 確定**: 複数ファイル送信を受け付ける方式に確定。 + 詳細は上記「3. 診断書提出」節(ファイル受信のたびに逐次アップロード、 + 「完了」ワードでProcess実行)参照 +- **固定メニューの表示方式・対話中のアクセス可否・リッチメニューとの選択 → 全て確定 + (2026-09-06表示方式確認、2026-09-07リッチメニュー不採用確定)**: + 「背景・設計転換」節参照。スマホ版は常時表示ボタン列(想定通り)、PC版は三本線アイコンから + 展開するポップアップ方式。対話中でもアクセス可能なことを確認済み。固定メニューのまま進め、 + リッチメニューへの切替は不採用 +- **LINEWORKS Bot Message API送信元認証の専用化・n8n実装の初回検証 → 完了(2026-09-06)**: + テスト用ワークフロー`HC-TEST: 添付アップロード検証`(`r5KQCKPedZFgsuIc`)で + PDF送信→ダウンロード→Pleasanterアップロード→確認メッセージ送信の一連を実機確認済み +- **固定メニュー登録(Persistent Menu API) → 完了(2026-09-06)**: `HC-SETUP: 固定メニュー登録` + ワークフロー(`0jVVF9WGlZrAygXX`)で4ボタンを実際に登録・実機確認済み +- **postback受信ペイロード構造 → 確定(2026-09-06、実機確認)**: 4ボタンを実際にタップし、 + Webhookペイロードが`{"type":"message","source":{...},"content":{"type":"text", + "text":"MENU_SCHEDULE"}}`のように通常のテキストメッセージと全く同じ形式で届くことを確認 + (execution id 331〜334、テストワークフロー`r5KQCKPedZFgsuIc`で受信)。「ボタン(`type:"message"`) + タップ時は通常のテキストメッセージと同じ形でBot Webhookに届く」という設計上の想定通り。 + HC-WA本体側では`content.type==="text"`かつ`content.text`が`MENU_*`のいずれかに一致する場合を + 固定メニュー起点の分岐として扱う実装でよい +- **重大な設計誤り発見・訂正 → 完了(2026-09-06)**: 対象者解決の設計が誤っていたことが判明。 + 詳細は`NodeSrv/docs/superpowers/specs/2026-09-05-healthcheck-lineworks-survey-n8n-design.md` + 2章「対象者解決の正しい照合ロジック」参照。LINEWORKS `source.userId`はメールアドレスではなく + UUID形式で、社員・組織マスタ管理テーブル(SiteId 504412)の`ClassA`⇔`Class011`経由の + 2段階照合が必要(本番HC-WAは未修正のまま、要反映) + +## 設計転換(2026-09-06): 固定メニュー方式からbutton_template方式へ + +固定メニュー(Persistent Menu)はPC版で三本線アイコンからの展開操作が必要と判明し、 +「トーク内にクリックするだけのボタンが出る」という当初のイメージと異なっていた。 +旧Express実装(`oldCode/express/modules/lineworksSurvey.js`)で使われていた +`button_template`(メッセージ本文にボタンを埋め込む方式)に転換する。 + +**実機検証済み(2026-09-06、テストワークフロー`HC-TEST: button_templateメッセージ送信検証` +`To6uN6mgFhh2D3dP`)**: `POST /v1.0/bots/{botId}/users/{userId}/messages`の`content`に +`{type:'button_template', contentText, actions:[{type:'message', label, text}, ...]}`を +指定して送信 → HTTP 201成功、トーク上でBotメッセージ本文の直下に4つの選択肢が +ボタンとして表示されることを確認。固定メニューのような「開く操作」は不要で、 +Botがメッセージを送ったその場でボタンが見える。旧「固定メニューpostback値」節の +内部コード(`MENU_SCHEDULE`等)ではなく、旧Express実装と同じくラベルとテキストを +一致させる方式(例: `{type:'message', label:'健診日程', text:'健診日程'}`)で検証した +(button_templateはその場の1メッセージに紐づく選択肢のため、手入力テキストとの +誤衝突リスクは固定メニュー常時入口方式より低いと判断)。 + +固定メニュー関連の検証記録(Persistent Menu API登録・PC/スマホ表示差異等)は +上記「背景・設計転換」「固定メニューpostback値」節に残すが、**採用方式はbutton_templateに +変更**。健康診断Botの固定メニュー自体は登録済みのままだが実質未使用になる +(副作用はないため放置。削除する場合は`HC-SETUP: 固定メニュー登録`と同じ要領で +空の`actions`を送信するか、Developer Console側で削除する)。 + +## 最終設計確定(2026-09-06): 固定メニュー(1ボタン)+button_template(3択)のハイブリッド方式 + +button_template方式は「Botが能動的にメッセージを送らないと選択肢が出ない」という課題がある +(Status変化時のHC-WPプッシュ通知では能動的送信で問題ないが、ユーザーが任意のタイミングで +自分からメニューを呼び出す手段がない)。これを解決するため、固定メニューとbutton_templateを +併用するハイブリッド方式に確定する(固定メニュー/リッチメニューは互いに排他だが、 +button_templateはメッセージコンテンツの一種で別レイヤーのため併用可能)。 + +**構成**: +1. 固定メニュー(Persistent Menu)はボタン1つだけに絞る: 「健康診断について」 + (`text`もラベルと同じ`健康診断について`。内部コード方式は不採用、button_template側と + 合わせてラベル直接方式に統一) +2. ユーザーが固定メニューから「健康診断について」をタップ → 通常のテキストメッセージ + (`content.type:"text"`, `content.text:"健康診断について"`)としてWebhookに届く +3. HC-WA側で`content.text === "健康診断について"`を検知 → button_templateで + 「ご用件を選んでください。」+3択(健診日程/受診・再検査報告/診断書提出)を返信 +4. 3択のいずれかをタップ → 各カテゴリの対話フロー開始(「3カテゴリの対話フロー詳細」節、 + postbackはラベルと同じ日本語テキストで受信する前提に統一。旧`MENU_SCHEDULE`等の + 内部コード方式は不採用) + +**実機検証済み(2026-09-06、Android実機、テストワークフロー`r5KQCKPedZFgsuIc`改修版)**: +固定メニュー「健康診断について」タップ→4択button_template表示、の一連の流れを確認済み +(検証時点ではストレスチェックを含む4択、その後対象外化しているため現行は3択)。 + +**固定メニューの更新**: `HC-SETUP: 固定メニュー登録`(`0jVVF9WGlZrAygXX`)の`persistentmenu` +登録内容を4ボタンから1ボタン(「健康診断について」)に変更済み(Webhook再実行でHTTP 201確認済み)。 + +**確定(2026-09-06)**: 3択button_templateは、固定メニュー「健康診断について」タップ +(`content.text==="健康診断について"`の完全一致)経由でのみ送信する。**待機状態なし +(`awaitInput:none`)で任意のテキストを送っても3択は送らない**(フォールバック方式は不採用)。 +理由: 任意テキスト受信への自動応答は、意図しないタイミング(雑談・誤送信等)でもメニューが +飛んでくる形になり、固定メニューという明確な入口を用意した意味が薄れるため。 +待機状態あり(`awaitInput`が`none`以外)での通常テキスト受信は、従来通りその待機中の +分岐処理(日付入力・はい/いいえ確認・ファイル受信等)に従う。 + +**未確定(今後の検討事項)**: +- HC-WA本体(`hc-wa-lineworks-response.json`)への正式反映は別タスク。現状はテスト + ワークフロー(`r5KQCKPedZFgsuIc`)上でのみ実装・検証済み + +## Bot送信メッセージ一覧・管理者からの自動送信(2026-09-06追記) + +`健康診断管理/プリザンターテーブル設計書_健康診断.xlsx`の「LINE自動送信」シートに、 +管理者からユーザーへ自動送信したいメッセージの実例6パターン(①受診日のお知らせ〜 +⑥再検査受診日当日)があり、これまでの固定メニュー/button_template設計(4カテゴリの +対話フロー)でカバーしていなかった要素を含む。同ファイルに新シート「メッセージ設定」を +追加し、メッセージID/名称/トリガー種別/トリガー詳細/条件分岐/差込変数/本文/ボタン/ +送信後処理の列で構造化・管理者が編集できる形にした(今後増える想定のメッセージも +このシートに追記していく運用)。 + +### 確定事項 + +- **①受診日のお知らせ**: Status 250→300(日程通知済み)への遷移そのものが + 「LINE送信済み」の状態を表す。別途フラグ列は不要 +- **②③⑥(日付ベースの自動リマインダー)**: Status変化ではなく「受診日の◯日前」 + 「受診日当日」「再検査受診日当日」という日付トリガー。現行のHC-WP(Webhookトリガー、 + クライアントスクリプト起動)ではカバーできないため、**新規にn8n専用ワークフロー + 「日次リマインダー送信」(仮称)を用意し、1日1回のSchedule Triggerで自動実行する**方針 + - リマインダー日数(②の「◯日前」)は全体共通の値とし、**n8nの`workflow_config_values`へ + 環境変数として保持する方針に確定**(例: `HEALTHCHECK_REMINDER_DAYS_BEFORE`。他の + `HEALTHCHECK_*`キーと同じ場所で一元管理。レコードごとの個別設定ではない)。 + **具体値は3日前に確定(2026-09-06)** + - **日次リマインダーワークフローの実行時刻 → 確定(2026-09-06)**: 毎日12:00に + Schedule Triggerで実行 + - **ワークフロー名 → 確定(2026-09-06)**: `HC-REMINDER`(既存のHC-SUB/HC-WP/HC-WAと + 命名規則を統一) + - **送信対象レコードの絞り込み条件 → 確定(2026-09-06)**: 「受診済み返信が来ていないもの」 + が共通条件。具体的には各リマインダーに対応するStatusのまま留まっているレコードのみを対象にする + (次のStatusへ進んだ=報告済みのレコードは自動的に対象から外れる) + - ②受診日アラート・③受診日当日: Status=310(日程確定済み)のまま + - ⑥再検査報告の催促: Status=800(再検査通知)のまま(「⑥の正しい仕様」節に訂正あり、 + 「受診日当日」ではない) + - ⑦日程未確定の催促(新規・2026-09-07追加): Status=300(日程通知済み)のまま。 + 起算点`Date004`(日程通知日)を1日経過するごとに繰り返し送信(「⑦日程未確定の催促」節参照) + - 日付一致の判定条件(「◯日前」「当日」の基準日をDate002等どの列から取るか)は + 日次リマインダーワークフロー実装時に詳細化する + +### ⑥の正しい仕様(2026-09-06訂正): 「再検査受診日当日」ではなく「再検査報告の催促」 + +当初「⑥再検査受診日当日(受診済み/未受診ボタン)」として設計していたが、実際のフローは +異なると判明した。 + +- ⑤再検査通知を送信 → ユーザーが自発的に「受診・再検査報告」(Status=800時の4項目 + アンケート、「2. 受診/再検査報告」節参照)で【再検査結果報告書】(a〜d)を報告する +- a.再検査受診日は事前の予定日ではなく、報告完了後に本人が事後入力する項目。 + 「再検査受診日当日」という独立したイベント自体が存在しない +- ⑥は正しくは**「⑤送信から2週間ごとに繰り返す、報告未提出者への催促」**。まだ報告 + (Status=800→810への遷移)が来ていない対象者に、2週間おきに定期送信する + +**催促の起算点管理(確定・2026-09-06)**: 508971に`Date003`(ラベル「再検査通知日」、 +`CheckA`直後に新規追加済み、スクリプト: +`.claude/js/site-scripts/健康診断管理/add-date003-508971.js`)を追加。 +**513156(実施年度-集計)へは追加しない**(508971のみでよいと確定済み。DescriptionGも +同様にテキスト集計不要のため513156には追加しない)。 + +- ⑤送信時(Status 500→800遷移、Process実行時のDataChanges)に`Date003`へ送信日をセット +- 日次リマインダーワークフロー(12:00実行)で`Date003 + 14日 = 今日`かつ`Status=800`の + レコードを検出したら催促送信 → 送信後、`Date003`を「今日」に更新(次回起算点をリセット、 + 以降14日ごとに繰り返す) +- Status=800→810(報告完了)になった時点で対象から自然に外れる(Statusチェックのみで足りる) + +**⑥のメッセージ文言(確定・2026-09-06)**: 「再検査の受診・ご報告はお済みでしょうか。 +まだの場合は固定メニューから「健康診断について」→「受診・再検査報告」よりご報告を +お願いいたします。」(ボタンなし、固定メニュー経由での自発的な報告を促す形) +- **①受診日のお知らせの差込変数 → 確定(2026-09-06)**: 医療機関=`Class003`(検査機関)、 + 日時=`Date001`(健康診断候補日、Status250→300時点はまだ本人未承諾のため確定日Date002ではなく + 候補日を使う)、コース=`Class004`(一般検診/節目検診/人間ドック)、自己負担金額=`Num001` + (ラベルは「窓口清算予定」のままだが、この用途に流用)。オプション内容は`Class007`単独では + なく、`Class005`(バリウム検査、胃カメラ/バリウム/検査不可の選択)+`Class006` + (追加オプション、希望する/希望しない)+`Class007`(オプション追加内容、`Class006`が + 「希望する」の場合のみ続けて表示)の3列を組み合わせて表示する + - **データ入力元 → 確認済み(2026-09-06、フェーズ1マスタ508959確認)**: 508959 + (LINEWORKSアンケート管理)ResultId 508965「【テスト】2026年度 健康診断」の + 質問マッピングJSON(`Description001`)で、`Class004`(節目検診・人間ドック希望)/ + `Class005`(バリウム検査について)/`Class006`(追加オプション希望有無)/`Class007` + (オプション希望内容、複数選択)/`DescriptionA`(その他希望)が実際にアンケート + 回答項目として定義済みと確認。これらは**フェーズ1(LINEWORKSアンケートフォーム回答→ + 508971反映)由来のデータ**。一方`Class003`(検査機関)・`Date001`(健康診断候補日)・ + `Num001`(自己負担金額)はこのマッピングJSONに含まれておらず、**会社側(管理者)が + 予約手配・金額計算した上で別途入力する項目**と判断できる(アンケートには社員本人が + 答えられない情報のため) + - **フェーズ1↔フェーズ2の紐付けキー一致確認**: 508959の`Class002`(ユーザー照合列)が + `"ClassC"`に設定されており、508971の`ClassC`(PleasanterUserId)と完全一致。 + フェーズ2の対象者解決ロジック(2章「対象者解決の正しい照合ロジック」)と同じキーを + フェーズ1も使っており、整合性が取れている +- **①⑤のメッセージ送信トリガー → 確定(2026-09-06)**: 既存のHC-WP(Webhookトリガー、 + 管理者が508971でProcessボタンを押した起点)で統一する。①は250→300、⑤は500→800の + Process実行時にHC-WPが起動し案内を送信する +- **②Class001(保険種類)未設定時の扱い → 確定(2026-09-06)**: 追加文言は付けない + (`Class001 === "東京土建"`の場合のみ追加、それ以外(空欄含む)は何もしない安全側の判定) +- **固定メニューがグループトークで押された場合 → 確定(2026-09-06、現行設計継続)**: + 特別な考慮はしない。個人対話前提のまま。実際に開発検証用グループへBotが招待された + 実績はあるが、Botとの1:1対話フロー自体はグループ内でも個人のuserId単位で成立するため、 + 想定外の対話フローになっても実害はないと判断 +- **⑤再検査通知の本文からテンプレ部分を削除 → 確定(2026-09-06)**: 【再検査結果報告書】 + のa〜d項目説明テンプレは、実際の4項目収集がBot対話(「2. 受診/再検査報告」Status=800時の + 順次アンケート)で行われることになったため不要。本文は案内のみに簡略化する + (具体的な新本文はExcel「メッセージ設定」シート⑤参照、「受診・再検査報告」の選び方を + 案内する形に変更) +- **①送信時の必須項目チェック → 確定(2026-09-06)**: 医療機関(Class003)・日時(Date001)等が + 未入力のまま管理者が①のProcessボタンを押せないよう、ProcessのValidateInputsで必須指定する。 + Process定義(250→300)の`ValidateInputs`に該当列を追加すること +- **複数レコードヒット時の異常系 → 確定(2026-09-06)**: 従来「Bot応答は保留」としていたが、 + ユーザーには何も返さず放置するのではなく、**エラー文言を返信する**方式に変更 + (例:「複数の対象レコードが見つかりました。お手数ですが管理者にお問い合わせください。」)。 + 6章「エラーハンドリング・異常系」の記述を更新すること +- **HC-SUBの役割整理 → 廃止・HC-WAへ吸収(2026-09-07確定)**: button_template転換により + 「4カテゴリいずれかの対話完了時にProcess実行+固定文言の結果案内」という役割はHC-WA + 自身で完結できるため、HC-SUBは独立したワークフローとして維持せず廃止し、HC-WAに機能を + 統合する。ワークフローが1つ減りシンプルになる +- **共通処理の実装方針 → 確定(2026-09-06)**: HC-WA/HC-WP/HC-REMINDERの間で共通する処理 + (504412照合ロジック・JWT取得等)はSub-workflow化せず、各ワークフローに個別実装する + (n8n運用ルールのノード数抑制方針と、Execute Workflow呼び出しのオーバーヘッド回避を優先。 + 既存のHC-WA/HC-WPも同様の重複実装方針) +- **`HEALTHCHECK_REMINDER_DAYS_BEFORE`の型 → 確定(2026-09-06)**: `workflow_config_values`の + 他のキーと同じ文字列型(`"3"`)で統一。n8n側で`Number()`変換して使用する +- **就業判定_可/不可(410→500/510) → 確定(2026-09-06)**: 自動判定ロジックはなし。 + 管理者/産業医が508971の`Class053`(就業判定)を見て、手動でProcessボタンを押して + 遷移させる(Bot対話フローには含まれない、管理者操作のみ) +- **診断書ファイル名規則 → 本番採用確定(2026-09-06)**: `{年度エリア(ClassZ)}_診断書_ + {名前(ClassC)}.{拡張子}`。「課題1」節に記載済みの規則をそのまま正式採用する +- **「完了」Process(500→900・810→900) → 管理者手動のみ(2026-09-07確定)**: 自動化は + 今回のスコープ外とし、管理者が手動でProcessボタンを押す運用に一本化する + (実装済みのProcess定義(Id13/14、「追加したボタン」実行種別)はこの方針と一致済み) +- **診断書ファイル名の重複対策 → 確定(2026-09-06)**: `{年度エリア(ClassZ)}_診断書_ + {名前(ClassC)}.{拡張子}`規則に、複数ファイル送信時は連番を付与する + (`..._1.pdf`, `..._2.pdf`のように送信順で採番) +- **LINEWORKS退職者からのメッセージ → 考慮不要(2026-09-06)**: 504412の`Check062` + (LINEWORKS在籍フラグ)による判定は不要。退職済みであればLINEWORKSアカウント自体が + 削除されておりBotへメッセージを送れないため、対象外ケースとして扱う必要がない +- **対象レコード0件(新入社員等) → 確定(2026-09-06)**: 固定メニュー「健康診断について」を + 押した際、508971に対象レコードが1件もない場合は「対象の健診情報がありません」と返信する + (複数ヒット時のエラー文言とは別扱い、正常系の一種として処理) +- **再検査通知(500→800)の判断基準 → 確定(2026-09-06)**: 管理者が508971の`Class052` + (健康診断結果)を見て手動判断する。就業判定と同様、自動判定ロジックはなし +- **日程変更(Date002再設定)時の管理者通知 → 不要(2026-09-06確定)**: 508971画面で + 確認できれば十分なため、別途通知の仕組みは作らない +- **診断書提出完了(Status=410)〜就業判定(500/510)の間 → Bot側は何もしない + (2026-09-06確定)**: 産業医・管理者の判断待ちの間、Botからのアクションは発生しない +- **同時進行セッション制限 → 確定(2026-09-06)**: 1ユーザーにつき1つの待機状態のみを + 想定する(`bot_conversation_state`は1レコード=1行のまま)。診断書提出フローと + 再検査報告4項目アンケートを同時に進行することはない(そもそも対象Statusが異なるため + 同時発生しない) +- **HC-WPの汎用化 → 確定(2026-09-06)**: ①⑤専用の実装にせず、Process名・DisplayName・ + `Description`(Pleasanter Process定義の実フィールド名。旧「ツールチップ」という呼称は + 誤りだったため訂正、376872実行予算WF申請の`processes.json`実例で確認済み)を`getsite`から + 取得してそのまま送信する汎用構造にする。新規の通知パターン(Excel「メッセージ設定」 + シートに今後追記される想定)が増えても、508971へProcessを1つ追加するだけでn8n側の + 改修なしに対応できるようにする(4節「フロー定義」の既存方針と同じ考え方をHC-WP全体に適用) +- **条件分岐文言の実現方式 → 確定(2026-09-06)**: ②「東京土建の人のみ追加文言」のような + 条件分岐は、`Description`欄に条件分岐込みのテンプレート文字列を書けるようにし、HC-WP側で + レコード値を見て解釈・置換してから送信する(具体的なテンプレート構文は実装時に確定。 + 例: `{{#if Class001=='東京土建'}}資格確認証を必ずご持参ください。{{/if}}`のような + 記法を想定)。`Description`は376872の実例では管理者向けメモとして使われていたが、 + 健康診断Botでは「Bot案内文言そのもの」を書く用途に転用する +- **④再提出依頼 → 確定(2026-09-06)**: Excelの丁寧な文言「診断結果をお送りいただきましたが、 + 画像での送信でしたので、PDFで再度お送り頂きますようおねがいいたします。」に統一する。 + 「3. 診断書提出」節のPDF判定失敗時メッセージ(旧「PDF形式でアップロードしてください」)を + この文言に置き換える +- **⑦日程未確定の催促(新規・確定、2026-09-07)**: ①日程通知(Status 250→300)を送信後、 + ユーザーが日程確定(固定メニュー「健診日程」から承諾/変更)に応答しないまま1日経過したら + 催促を送信する。以後も未確定の間は1日ごとに繰り返し送信する(⑥再検査報告の催促と同じ + 「起算点を都度リセットして一定間隔で繰り返す」方式、間隔だけ2週間→1日に短縮) + - 起算点管理: 508971に新規列`Date004`(ラベル「日程通知日」、`Date003`と同様の運用)を追加 + (空き列で確認済み、2026-09-07)。Process「日程通知済み」(250→300)実行時のDataChanges + で`Date004`へ送信日をセットする + - 日次リマインダーワークフロー(HC-REMINDER、毎日12:00実行)で`Date004 + 1日 = 今日`かつ + `Status=300`のレコードを検出したら催促送信 → 送信後、`Date004`を「今日」に更新 + - Status=300→310(日程確定)になった時点で対象から自然に外れる(Statusチェックのみで足りる) + - メッセージ文言はExcel「メッセージ設定」シートに追記する(本節では扱わない) +- **⑤再検査通知**: Status 500→800(再検査通知)。返信は「a.再検査受診日/b.受診医師名/ + c.検査項目、結果(概要)/d.就労の可否」の4項目テキスト入力形式。既存のawaitInput + (`date`/`file`/`yesno_confirm`/`schedule_confirm`)にない新パターンのため、 + 4項目をまとめて受け付ける新しい待機状態(例: `report_form`)の追加が必要。 + **保存先列 → 確定・反映済み(2026-09-06)**: a〜dの4項目は個別列に分けず、ユーザーが + 入力したテキストをまとめて1つの欄にそのまま保存する方式。508971の`Class055` + (再検査就業判定)の直後に`DescriptionG`(ラベル「再検査報告」)を新規追加済み + (スクリプト: `.claude/js/site-scripts/健康診断管理/add-descriptionG-508971.js`)。 + なお`CheckA`(ラベル「再検査連絡」)が既存で確認済みのため、⑤送信後の「再検査連絡に + チェック」処理はこの`CheckA`を`true`にすればよい +- **東京土建の人のみの追加文言(②)→ 確定**: 508971の`Class001`(ラベル「保険種類」、 + `ChoicesText:"東京土建\n協会けんぽ"`)で判定。`ClassHash.Class001 === "東京土建"`の場合のみ + 「資格確認証を必ずご持参ください。」を本文末尾に追加する + +## 別途対応済み(今回のBot専用化に伴う設定変更) + +- `workflow_config_values`へ健康診断専用キー4件追加: + `HEALTHCHECK_LW_CLIENT_ID` / `HEALTHCHECK_LW_CLIENT_SECRET` / + `HEALTHCHECK_LW_SERVICE_ACCOUNT` / `HEALTHCHECK_BOT_ID`(=13078741) +- `LINEWORKS_BOT_SECRET`を実値へ更新(Callback URL設定用) +- Pleasanter 484184(LINEWORKS Bot管理)へ「健康診断Bot」レコード登録(ResultId 513223) +- n8n Credential新規作成: 「LINEWORKS Bot Private Key(健康診断Bot専用)」 + `o2f2ljohvI62iBOv`(ユーザーがn8n UI上で手動登録) +- HC-SUB/HC-WAのJWTノード・コード内の認証情報参照(`LW_BOT_CLIENT_ID`等の共用キー、 + Credential`Hw0qlEaGfLPnQWp1`)を上記の専用キー・専用Credentialへ差し替え → **完了 + (2026-09-07)**。下記「n8n実装(フェーズ2)完了」節参照 + +## n8n実装(フェーズ2)完了(2026-09-07) + +本ファイルの確定設計に基づき、n8nワークフロー本体を全面実装した。トークにBotを送る +利用者がまだ存在しない(フェーズ2運用未開始)ことを確認した上で、本番稼働中だった +旧設計(Express移植直後のpendingProcesses方式)を安全に全面書き換えした。 + +### 実装済みワークフロー + +| ワークフロー | ID | 状態 | 内容 | +|---|---|---|---| +| HC-WA: LINEWORKS応答受信 | `0i0Ze3Gq0Waof3Fa` | active、11ノード | 全面書き換え。対象者解決2段階照合(504412 ClassA⇔Class011)・固定メニュー/button_template・3カテゴリ絞り込み・awaitInput拡張(none/date/file/schedule_confirm/yesno_confirm/report_form)・担当者連絡導線・診断書添付ダウンロード+Pleasanterアップロード・キャンセル共通仕様・3回失敗強制中断・10分タイムアウト対応(lastFileReceivedAt記録) | +| HC-WP: Statusプッシュ通知 | `swXfpoDtwZDT3Hwk` | active、8ノード | 全面書き換え。Process定義の`Description`欄をテンプレートとして解釈(プレースホルダー置換+`{{#if 列名=='値'}}`条件分岐)、504412経由の宛先解決、専用認証キーで送信。HC-SUB非依存化 | +| HC-SUB: プロセス実行と案内送信 | `XRqcykbG2LuAjGG2` | **廃止・deactivate** | 役割をHC-WA/HC-WPへ吸収(design-fixed-menu.md「HC-SUBの役割整理」確定事項通り)。ワークフロー自体は削除せず無効化のみ | +| HC-SETUP: 固定メニュー登録 | `0jVVF9WGlZrAygXX` | active、編集済み・**実行は未実施** | actions配列を「健康診断について」1ボタンから「健康診断について」「担当者に連絡」の2ボタンへ編集済み。本番の固定メニューへの反映(Webhook実行)はユーザー確認の上、後日実施 | +| HC-REMINDER: 日次リマインダー送信 | `kyyd1BHpUJkJSN3W` | **新規作成、inactive** | 毎日12:00 Schedule Trigger。②受診日アラート(Date002-3日)・③受診日当日(Date002当日、返信をyesno_confirm(attendance_310)として予約)・⑥再検査報告の催促(Date003+14日、送信後Date003更新)・⑦日程未確定の催促(Date004+1日、送信後Date004更新)を統合 | +| HC-DOC-TIMEOUT: 診断書提出10分タイムアウト監視 | `ayXtZbP2jqVXaWyR` | **新規作成、inactive** | 10分間隔Schedule Trigger。`lastFileReceivedAt`から10分経過したawaitInput=file行を検出し、添付1件以上ならProcess`診断書_保存`実行+通常の完了メッセージ、0件なら対象外のまま | + +### 【バグ修正】HC-WPの「検証」ノードでheaders/body参照先が誤っていた(2026-09-07発見・修正) + +実データでの動作テスト中、HC-WP Webhookを叩くと必ず`Unauthorized: invalid API key` +(修正後は`Bad Request: resultId, processId は必須です`)で失敗する不具合を発見。原因は +「検証」ノードのコードが`$input.first()`(=直前ノード「設定値一括取得」の出力、 +`{configKey, configValue}`形式の行データ)を参照していたため、`headers`も`body`も +存在せず常に検証に失敗していた。実際にWebhookが受信した`headers`/`body`を得るには +`$('Webhook').first()`と明示的にノード名指定する必要がある(HC-WA等では正しくこの形に +なっていたが、HC-WPの「検証」ノードだけ書き換え漏れがあった)。`$('設定値一括取得').all()`で +値自体は正しく取得できていた(Data Table Get自体は正常)ため、APIキーの値そのものを +疑って文字コードレベルまで比較して初めて気づいた。修正後、HC-WP Webhook(resultId=513409, +processId=5)で`{"result":"ok"}`が返り、実機で正常動作を確認。 + +### 【未解明】ProcessId明示指定でのAPI実行時、一部DataChangesが発火しない + +実データテスト中、実行種別「追加したボタン」のProcess(Id1〜3等)は`ProcessId`を指定した +API実行でNum列DataChangesが正しく発火したが、以下の2ケースでは発火しなかった: + +- 実行種別「追加したボタン/作成・更新」(ExecutionType:20)のProcess(Id4:検査機関確定): + ProcessId明示実行でStatus遷移(250→260)は成功したが、`Num047`のDataChanges + (InputValue型)は発火しなかった +- Id5(日程通知済み): `Num035`(InputValue型)は発火したが、`Date004`(InputDate型)は + 発火しなかった + +InputValue型とInputDate型で挙動が違う、あるいは実行種別によって挙動が違う可能性があるが、 +未検証・未解明。回避策として、これらのDataChangesが必要な場合は該当列を`DateHash`/ +`NumHash`で明示的に直接updateする運用でカバーできる(実データ検証時に実施)。今後、 +本番運用(特に260への自動遷移・①日程通知の`Date004`起算点管理)でこの制約が問題になる +場合は、n8n側でProcess実行後に該当列を明示更新する処理を追加する等の対応を検討すること。 + +### Data Table `healthcheck_bot_state`(`jqMDa2YZTI4f0iQ7`)の変更 + +列構成を新設計に合わせて変更済み: `targetEmail`→`lineworksUserId`にリネーム、`pendingProcesses`列削除、 +`formDraft`・`failCount`・`lastFileReceivedAt`列を新規追加(最終列構成: resultId, lineworksUserId, +currentStatus, awaitInput, awaitProcessId, awaitColumn, formDraft, failCount, lastFileReceivedAt)。 +変更時点で既存データ0件のため移行作業は不要だった。 + +### 未実施・残作業 + +- ~~HC-SETUPの実行(固定メニュー2ボタン化を本番へ反映)~~ → **完了(2026-09-07)**。 + 固定メニューは「健康診断について」「担当者に連絡」の2ボタン構成で本番反映済み + (Webhook実行、HTTP 201確認済み) +- ~~LINEWORKS Bot API疎通確認~~ → **完了(2026-09-07)**。野木健一郎さん(PleasanterUserId + 460)宛に、504412照合→専用Credentialでのアクセストークン取得→LINEWORKS Bot API送信、 + という新実装の中核経路で一時テストワークフロー経由のテキスト送信を実施し、実機のトークで + 受信を確認済み(テストワークフローは実行後deactivate) +- 実データでの動作確認(各Process実行・各awaitInput分岐・添付アップロード等、Bot対話の + 一連の流れそのもの)は未実施 +- HC-REMINDER・HC-DOC-TIMEOUTのactivate(Schedule Trigger起動)は、実運用開始タイミングに + 合わせて別途判断 +- `workflow_config_values`の`HEALTHCHECK_REMINDER_DAYS_BEFORE`(=3)は今回追加済み +- `NodeSrv/apps/n8n/docs/n8n-guide.md`(機密ファイル)を今回の変更内容 + (HC-WA/HC-WP全面書き換え・HC-SUB廃止・HC-SETUP実行・HC-REMINDER/HC-DOC-TIMEOUT新規作成・ + 専用Credential使用・healthcheck_bot_state列構成・`PLEASANTER_MASTER_SITE_ID_PROD`の + 誤記訂正)に合わせて更新済み + +### 【方式転換】日程通知メッセージの組立を508971側(クライアントスクリプト)に一本化 + +n8n(HC-WP)側で`items/get`によりProcess.Descriptionのテンプレートを解決する実装 +(`resolve.js`のfillTemplate)では、Class003(検査機関)等の外部マスタ項目がResultId +(内部値、例:「511926」)のまま返り、表示名(例:「日の出ヶ丘病院」)に変換されない不具合が +あった(実機のLINEWORKSトークで「医療機関:511926」と表示される形で顕在化)。 + +対応として、n8n側でのテンプレート解決をやめ、**508971のクライアントスクリプトで、担当者が +検査機関確定情報を保存する時点(画面に表示名が確定している時点)で通知文を組み立て、 +`DescriptionB`列に保存しておく**方式に変更した。日程通知(HC-WP)は組立済みの`DescriptionB` +の値をそのまま送信するだけでよく、n8n側でのマスタ名称解決が不要になり確実性も上がる。 + +実装内容(2026-09-07、508971へ反映済み): + +- `Pleasanter/健康診断管理/configs/production/site-508971_社員別健康診断管理/scripts/2_検査機関確定通知文組立.js` + (クライアントスクリプト新規追加): + `$p.events.before_send_Update`で発火。`Class003`(検査機関)・`Class004`(コース)・ + `Class005`(バリウム検査)・`Class007`(オプション追加内容、複数選択)は + `$('#Results_' + ColumnName).find('option:selected')`の`.text()`で選択中の表示名を + 取得(複数選択は`、`区切りで結合)。`Class006`(追加オプション、希望する/希望しない、 + マスタ参照でない選択肢)も同じ`select`パターンで取得。`Date001`・`Num001`は + `$p.getControl(...).val()`で取得(画面表示形式のままでよいため変換不要。ただし`Num001` + はカスタム書式適用済みで既に`¥0`のような通貨記号付き文字列になっているため、末尾に + 「円」を追加すると二重表記になる → 追加しない、そのまま出力に修正済み)。 + 検査機関・日程・コースの3項目が揃っていれば組み立てて`$p.set($p.getControl('DescriptionB'), ...)` + で保存。 +- 508971のサイト設定に`DescriptionB`列を新規追加(`LabelText: "検査機関確定通知文"`)、 + `EditorColumnHash.General`の`DescriptionA`の直後に配置。**Processesには一切触れていない** + (OnClickフィールド事故の教訓を踏まえ、Columns/EditorColumnHash/Scriptsへの追記のみで、 + Process配列は現状の17件をそのままコピーして送信)。 +- 反映用スクリプト: + `Pleasanter/.claude/js/site-scripts/健康診断管理/add-clientscript-notice-508971.js` + (新規追加時)、`update-clientscript-notice-508971.js`(Body更新時)。いずれも + `--execute`未指定時はプレビューのみで実際には送信しない安全策付き。 +- 動作確認: 513409(野木健一郎さん、検査機関=日の出ヶ丘病院で確定済みのレコード)で + Seleniumから保存ボタン(`#UpdateCommand`)をクリックし、`items/get` APIで + `DescriptionHash.DescriptionB`を直接確認。以下の内容で正しく組み立てられることを確認済み: + ``` + 医療機関:日の出ヶ丘病院 + 日時:2026/09/08 10:11 + コース:その他:そのた1 + バリウム検査:その他:そのた2 + 追加オプション:希望する(その他:そのた3、便潜血、胃カメラ、前立腺、子宮頸がん) + 自己負担金額:¥0 + + 変更がありましたらこちらのLINEに連絡ください + ``` +- staging環境はこのプロジェクトに存在しない(`健康診断管理/configs`は`production`のみ)ため、 + 本番508971へ直接検証した。Processesを一切変更しない種類の変更(Columns追加・ + EditorColumnHash配置・Scripts追記)であり、過去のOnClick事故(Processオブジェクトを + ゼロから構築した場合のみ発生)とはリスクの種類が異なると判断した。 + +**完了(2026-09-07)**: HC-WP(`swXfpoDtwZDT3Hwk`、ノード「レコード・Process・宛先解決」)の +`resolve.js`からfillTemplate・`getsite`呼び出し・Processes全件検索ロジックを撤去し、 +`ProcessId`ごとに固定の取得方法(Id5:検査機関確定通知 → `record.DescriptionHash.DescriptionB` +をそのまま使用、Id11:再検査通知 → 固定文言をn8nコード内に直書き)で`messageText`を組み立てる +実装に簡略化してPUT反映済み(`updatedAt: 2026-09-07T11:25:20.492Z`)。これにより508971への +`getsite`呼び出し(Columns/Processes全件を含む重いレスポンス)が不要になった。 + +**残作業**: 今後Id5・Id11以外のProcessでもHC-WP経由の通知が必要になった場合、 +`resolve.js`の`FIXED_MESSAGES`または`ProcessId`分岐に追記が必要(対応する +Description列を508971側にも用意すること)。 + +### 【設計確定】日程通知→日程確定は「キャッチボール」として自動連続、他カテゴリは非連続(2026-09-07) + +固定メニュー「健康診断について」タップ起点の対話フロー(164-165行目参照)とは別に、 +**①日程通知(HC-WP、ProcessId=5実行時)の直後は、日程確定/変更のbutton_template +(承諾/来週も同じ曜日/再来週も同じ曜日/担当者に連絡)も自動で続けて送信する**方式に +確定した。本人が固定メニューから「健診日程」を選び直さなくても、通知を受け取った流れの +まま即座に承諾・変更できる(「日程通知↔日程確定はキャッチボールなので連続してよい」との +判断)。**受診・再検査報告/診断書提出等、他のカテゴリはこれまで通り非連続**(本人が +固定メニューから能動的にアクセスする既存フローのまま)。 + +実装内容(2026-09-07、HC-WP `swXfpoDtwZDT3Hwk`へ反映済み、8→10ノードに拡張): + +- `resolve.js`(ノード「レコード・Process・宛先解決」): 返却値に`processId`・ + `date001`(`record.DateHash.Date001`)・`currentStatus`(`record.Status`)を追加 +- `send.js`(ノード「アクセストークン取得・LINEWORKS送信」): 通知文(`messageText`)を + text送信した直後、`processId===5`の場合のみ、HC-WA「健診日程」タップ時と同一の + button_template(`sendButtons`相当、`formatDateJp`/`addDaysIso`ロジックをこのノード内に + 複製)を続けて送信するよう拡張。送信後は`nextAwaitInput="schedule_confirm"`・ + `nextFormDraft=JSON.stringify({date001})`を返す(`processId!==5`なら`"none"`のまま) +- 新規ノード「IF: 日程確定待ちにするか」(`nextAwaitInput === "schedule_confirm"`で分岐)→ + Trueの場合のみ新規ノード「日程確定待ち状態セット」(`n8n-nodes-base.dataTable`、 + `healthcheck_bot_state`へupsert、HC-WA「状態更新」ノードと同じ列構成)を実行してから + Respond to Webhookへ。Falseはそのまま直接Respond to Webhookへ +- 動作確認済み(513409、野木健一郎さん): Webhook実行→通知文+button_templateが + 連続してLINEWORKSトークに届くことを確認。`healthcheck_bot_state`に + `awaitInput: "schedule_confirm"`, `formDraft: {"date001":"2026-09-08T10:11:00"}`が + 正しくupsertされることも確認済み + +### 【未解決・次回続き】HC-WA側「承諾」応答処理でエラー、原因はn8n Task Runnerの`crypto`制限 + +上記のbutton_templateで「承諾」を押しても、HC-WA(`0i0Ze3Gq0Waof3Fa`)側の処理が +エラーで止まり、Pleasanter側(Date002・Status)が更新されない不具合が発生し、 +**未解決のままセッション終了**。 + +**判明した原因**: HC-WAの「署名検証・対象レコード特定」ノード(`identity.js`相当)で +LINEWORKS Webhookの署名検証(HMAC-SHA256、`x-works-signature`ヘッダー検証)に +`require("crypto")`を使っていたが、n8nの実行環境がTask Runner方式に変わっており、 +Codeノードから`require`でNode.js組み込みモジュールを読み込むこと自体が許可されなくなっている +(`Error: Module 'crypto' is disallowed`)。 + +**1回目の対処(不十分だった)**: `require("crypto")`をやめ、requireなしで使えるグローバルの +Web Crypto API(`crypto.subtle.importKey`/`crypto.subtle.sign`でHMAC-SHA256計算)に +書き換えてPUT反映した。しかし再テストすると別のエラー`crypto is not defined [line 24]`と +なり、**このn8n Task Runner環境ではグローバルの`crypto`オブジェクト自体も公開されていない** +と判明した(Web Crypto API化だけでは解決しない)。 + +**次回セッションで検討すべき対応案**(いずれも未着手、方針未確定): +1. **pure JS実装への置き換え**(推奨候補): requireにもグローバル`crypto`にも依存しない、 + JavaScriptのみで書かれた軽量なHMAC-SHA256/SHA-256実装(60〜100行程度)をコード内に + 直接埋め込む。ワークフロー構造・Credential追加が不要で、このノード内だけで完結する +2. **n8n組み込みCryptoノード(`n8n-nodes-base.crypto`)を使う**: HMAC操作に対応しているが、 + 専用のCrypto Credential(Hmac Secret)を別途作成する必要があり、Webhookの生ボディ + (バイナリ)を正しく渡せるか要検証。ワークフロー構造の変更(ノード追加)を伴う +3. **インフラ側でNode.js組み込みモジュールを許可する** + (環境変数`NODE_FUNCTION_ALLOW_BUILTIN=crypto`等をn8nのTask Runner側に設定): + 影響範囲がn8n全体(他の全ワークフロー)に及び、コンテナ再起動が必要になる可能性が + 高いため、最終手段として位置づけ。まずは1.のコード側対応を優先すべき + +**セッション終了時点の状態(次回再開時の前提)**: +- HC-WA(`0i0Ze3Gq0Waof3Fa`)は現在このバグを含んだまま本番稼働中(`active: true`)。 + 署名検証に失敗する=**現状は本人からの返信を一切処理できない**状態 +- 513409(野木健一郎さん): Status=300のまま、Date002は未設定(`1899-12-30`=未設定 + センチネル値のまま)。`healthcheck_bot_state`の該当行は`awaitInput: "schedule_confirm"` + のまま(未消費)。次回、identity.js側のcrypto問題を解決した後にもう一度「承諾」を + 送信すれば、このまま処理が流れるはず(Pleasanter側・Data Table側の状態リセットは + 不要、そのまま続きから検証できる) +- HC-WP側(日程通知→button_template連続送信)は正常動作を確認済み、今回のバグとは無関係 +- n8n実行履歴: HC-WA実行ID 363・364が`status: error`(`lastNodeExecuted: "署名検証・対象レコード特定"`) + +## Botトークフロー再設計(2026-09-10確定・2026-09-11実装完了) + +`2026-09-10-bot-flow-redesign.md`(承認済み仕様書)・`2026-09-10-bot-flow-redesign-plan.md` +(実装計画)に基づき、「健康診断メニュー」3択(受診日時について/受診結果について/ +再検査について)への呼称統一とパターンA(受診日時)・パターンB(受診結果確認)の +新規実装を全面反映した。実装前に判明した以下2件の緊急バグも合わせて修正済み。 + +### 実装前に修正した緊急バグ2件(2026-09-11) + +- **resultId型エラーによる無限リトライ**: 「署名検証・対象レコード特定」ノードが + `resultId`をPleasanter ItemIdのNumber型のまま渡していたため、後段「IF: resultIdあり」 + ノード(`typeValidation:strict`のstring.notEmpty演算子)が型エラーでワークフロー全体を + 異常終了させ、Webhookレスポンス未返却→LINEWORKS側リトライ→同一メッセージ重複送信を + 引き起こしていた。`resultId: String(record.ResultId)`に修正 +- **LINEWORKS自動送信イベント"利用開始"への無条件反応**: LINEWORKSがBotとのトーク開始・ + グループ招待時に自動送信する定型テキスト`"利用開始"`を、ユーザーの実発言と誤認識して + 無条件応答(「対象の健診情報がありません」「待機状態が不正です」等)を返していた。 + `content.text === "利用開始"`を検知し`resolveError: "system_event"`として無反応にする + 分岐を追加 + +いずれも発覚時点で健康診断関連Webhook全件(HC-WA/HC-SETUP/HC-FORM-SYNC/HC-WP)を +一時deactivateして対応した。 + +### 実装内容(HC-WA `0i0Ze3Gq0Waof3Fa`、ノード「分岐処理・実行・送信」) + +- `PROCESS_ID.SELF_ARRANGE`(自分手配、508971 Process Id:18)を追加 +- `replaceDateLineInDescription`/`formatDateTimeSlash`: DescriptionBの「日時:」行のみを + 確定日で書き換えるヘルパー +- `buildHospitalContactMessage`: 511924(受診病院一覧)をResultIdで1件取得し、病院名 + (ClassB)・電話番号(ClassD)・担当者(ClassF、任意)を組み立てて返す。1件にヒット + しない場合は「検査機関へお問い合わせください。」にフォールバック +- 「受診日時について」(Status 300: 候補日提示+承諾/受診日時の変更/自分手配、 + Status 310: DescriptionB照会+受診日時の変更) +- 「受診結果について」(Status 310: 受診完了/未受診、Status 400: 診断書提出フローへ直結) +- 「再検査について」(Status 800: 「受診・再検査報告」ボタンへの橋渡し) +- 新規awaitInput状態`retest_choice`(未受診→再手配/自分手配の選択)・ + `reschedule_input`(再手配時の新日時自由入力)を追加 +- `yesno_confirm`をpurpose横断(schedule/reschedule/attendance_310)の共通OPTIONS方式へ + リファクタ +- `confirmSchedule`共通関数化(承諾ルート・自由入力確定ルートの両方から呼ぶ、 + スコープバグ回避のためトップレベル関数として定義) + +### HC-REMINDER(`kyyd1BHpUJkJSN3W`、ノード「対象レコード取得・分類」) + +`kind:"three"`(受診日当日リマインダー)のボタンラベルを`はい/いいえ`から +`受診完了/未受診`へ変更(HC-WA側の`attendance_310`判定と統一)。 + +### Process「自分手配」(508971 Id:18) の設計修正 + +当初CurrentStatus=250で登録したが、Bot対話フロー(パターンA: Status=300、 +パターンB未受診: Status=310)からの呼び出しではCurrentStatus不一致で必ず400エラー +(「要求が不正です」)になることが実機テストで判明。**CurrentStatus=-1(任意)+ +条件タブ`Status in [300, 310]`**に変更して解消(ユーザーがPleasanter画面上で修正)。 + +### 実装漏れバグ(実機テスト中に発見・修正) + +待機状態の妥当性検証リスト(`["none","date","file","schedule_confirm","yesno_confirm","report_form"]`) +に新規追加した`retest_choice`・`reschedule_input`を含め忘れており、未受診→再手配ルートの +2ステップ目で「待機状態が不正です」により強制リセットされる不具合があった。検証リストへ +両状態を追加して解消。 + +### 実機テスト結果(2026-09-11、野木健一郎さん 513409、全シナリオ成功) + +- シナリオA1(承諾ルート): Status 300→310、Date002確定、DescriptionB書換確認 +- シナリオA2(変更→自由入力→承諾): 検査機関連絡先表示→日付パース→確認→確定、Date002/DescriptionB正しく更新 +- シナリオA3(自分手配): Status 300→400、Class003=513480(自分手配) +- シナリオA4(パース失敗3回): 3回目で強制中断メッセージ確認 +- シナリオB1(受診完了): Status 310→400 +- シナリオB2(未受診→再手配): 検査機関連絡先表示→新日時確定、Status 310のまま(ProcessId無し更新)維持確認 +- シナリオB3(未受診→自分手配): Status 310→400、Class003=513480 +- シナリオB4(診断書提出フローへの導線、Status=400時): 既存フロー無傷、PDF受付確認 + +テスト中、511924(受診病院一覧)のマスタが実機テスト当日に再構築されており、513409の +既存Class003値(旧ResultId)が失効していたため、テスト時は514112(実在値)へ一時的に +書き換えて検証した(テスト後、スナップショットで元の値に復元済み)。 + +### 未対応事項 + +- 診断書提出フロー(シナリオB4)のテストで実際にPDFファイル1件を513409へ添付した + (既存フローの動作確認が目的、削除要否は次回判断) +- HC-REMINDER`kind:"seven"`(⑦日程未確定の催促)のメッセージ文言内に旧ラベル + 「健診日程」への参照が残っている(`固定メニューから「健康診断について」→「健診日程」より`)。 + ボタンラベル変更により古くなっているが、計画のスコープ外のため未修正