diff --git a/NodeSrv/docs/superpowers/plans/2026-09-05-org-master-sync-n8n-production-migration.md b/NodeSrv/docs/superpowers/plans/2026-09-05-org-master-sync-n8n-production-migration.md new file mode 100644 index 00000000..976fdf5c --- /dev/null +++ b/NodeSrv/docs/superpowers/plans/2026-09-05-org-master-sync-n8n-production-migration.md @@ -0,0 +1,123 @@ +# org-master-sync n8n production移行 設計メモ + +2026-09-04〜進行中。org-syncのn8nワークフロー群をproduction(本番プリザンター `nextoffice.next-hd.co.jp`)向けに構築する作業の設計意図・進捗記録。適宜追記していく。 + +## 1. 背景: 「模倣(Mock)テーブル」の設計意図 + +n8nの`org-sync`系ワークフロー(①③④④-1④-2④-3)は、Pleasanter標準のUsers/Depts/Groups API(`/api/users/get`等)ではなく、staging環境に用意した「模倣テーブル」(SiteId 504737=ユーザー, 504736=組織, 504738=グループ、いずれも`ReferenceType: Results`の通常テーブル)を参照して構築されていた。 + +**意図(本人確認、2026-09-05)**: 開発当時は本番データを直接いじれなかったため、模倣テーブルでフロー構築を先に進め、「宛先(SiteId/BASE_URL)を変更するだけで本番化できる」ことを狙った設計だった。 + +**判明した実態(2026-09-05調査)**: この狙いは実現できていなかった。 +- 模倣テーブルはPleasanterのitems/export形式(表示名キーのJSON/CSV、例: `ログインID`, `組織`)を返すが、本物の標準API(`/api/users/get`, `/api/depts/get`, `/api/groups/get`)はシステムフィールド名(`UserId`/`LoginId`/`DeptId`/`GroupId`等)+Offset/TotalCountページング必須という全く別の形式。「宛先を変えるだけ」では動かず、レスポンス変換層の追加が必要だった +- LWグループマッピングテーブル(SiteId 504411、66件: 組織48・役職8・職級6・利用権限タイプ4)の「グループID(」列は模倣Groupsテーブル内の独自連番(1〜135+)で、本物のPleasanter Groups(GroupId実在範囲5〜69、全59件)とは無関係。名前での突合でも一致0件=本物のGroupsには1件も対応物が無い + +## 2. n8nワークフロー構成(2026-09-04時点で判明した正式版一覧) + +Proto系(`org-master-sync prototype`フォルダに格納済み、検証用として維持、変更しない)とは別に、正式版として以下が存在: + +| ワークフロー | ID | +|---|---| +| org-sync: ① LINEWORKSマスタ→プリザンターマスタ情報更新 | pJBj07V5p4Av8f6E | +| org-sync: ② バックアップ | HuKpZQEQeU4iUCUf | +| org-sync: ③ マージマスターメイン処理 | ScoUpIshCsdO2KdI | +| org-sync: ④ プリザンターマスタ更新(④-0〜④-3の統合版) | 82y3S515PbEBLSDv | +| org-sync: ④-0 状態判定 | UQ7vtaajy9O93jhW | +| org-sync: ④-1 新規ユーザー作成 | 6NwZXgXY2eK1Wt4g | +| org-sync: ④-2 ユーザー更新・退職処理 | I9NimeonT8rM51KW | +| org-sync: ④-3 グループ更新 | QPt4JRHOgSuN0nnS | + +## 3. staging/production分離(2026-09-04実施) + +上記8本を複製し、`org-sync: staging: ...`(既存の8本をリネーム)と`org-sync: production: ...`(新規複製8本)に分離。 + +- production版はdataTable取得ノードのconfigKeyを`_TEST`→`_PROD`へ全置換(④-0〜④-3内の「設定値統合」Codeノードにハードコードされてた`_TEST`参照も含む) +- production版のWebhookパスは全部`-prod`サフィックスで一意化(例: `org-sync-01-lw-master-update-prod`) +- production版8本は全部`active: false`(未有効化)で作成。**稼働テストは今回のスコープ外、明示許可があるまで有効化しない** +- フォルダ構成: `org-master-sync`(production版8本)/ `org-master-sync staging`(staging版8本)/ `org-master-sync prototype`(Proto系旧版15本)/ `LINEWORKS`(通知系2本) + +## 4. `workflow_config_values`(n8n Data Table)のPROD値修正 + +旧`_PROD`値(`PLEASANTER_MASTER_SITE_ID_PROD=502552`等)はいずれも404で存在しない廃止済みSiteIdだった。TESTが既に`504406〜504412`系列を使っていたのに対応させ、以下へ更新(2026-09-04、UI手動編集で完了): + +| キー | 更新後の値 | +|---|---| +| PLEASANTER_MASTER_SITE_ID_PROD | 504412(社員・組織マスタ管理テーブル) | +| PLEASANTER_POSITION_MASTER_SITE_ID_PROD | 504408(LINEWORKS:役職マスタ) | +| PLEASANTER_LEVEL_MASTER_SITE_ID_PROD | 504410(LINEWORKS:職級マスタ) | +| PLEASANTER_USERTYPE_MASTER_SITE_ID_PROD | 504409(LINEWORKS:利用権限マスタ) | +| PLEASANTER_BACKUP_SITE_ID_PROD | 504407(社員・組織マスタ管理(副・バックアップ用)) | +| PLEASANTER_LW_GROUP_MAP_SITE_ID_PROD | 504411(LWグループマッピング、新規追加) | + +n8n Data Table Public APIは行のupdate/delete専用エンドポイントが存在しない(PATCH/PUT/DELETEいずれも404か405、POSTのみ実装)。UI側の値セルはダブルクリックで編集・削除可能と判明(削除もUIから実施できた)。 + +## 5. 模倣テーブル→本物API移行の対応状況 + +| ワークフロー | 参照する模倣テーブル | 対応状況 | +|---|---|---| +| ③ マージマスターメイン処理(production版のみ) | Users(504737), Depts(504736) | **完了(2026-09-05)**。下記5-1参照 | +| ① LINEWORKSマスタ→プリザンターマスタ情報更新(production版のみ) | Groups(504738) | **完了(2026-09-05)**。下記5-2参照 | +| ④ プリザンターマスタ更新(統合版) | Users, Depts, Groups全部 | **完了(2026-09-05)**。下記5-3参照 | +| ④-1 新規ユーザー作成 | Depts, Groups, Users(create先も504737) | **完了(2026-09-05)**。下記5-4参照 | +| ④-2 ユーザー更新・退職処理 | Depts, Users, Groups | **完了(2026-09-05)**。下記5-4参照 | +| ④-3 グループ更新 | Groups | **完了(2026-09-05)**。下記5-4参照 | + +これで①②③④④-0④-1④-2④-3、production版8本すべての模倣テーブル参照が解消された。 + +### 5-1. ③の対応内容(production版のみ、staging版は変更していない) + +- `プリザンターUsers取得`ノード: `POST items/504737/export` → `POST api/users/get`(`View:{ApiGetMailAddresses:true}`付き、Offset+TotalCountページング設定済み) +- `プリザンターDepts取得`ノード: `POST items/504736/export` → `POST api/depts/get`(同上ページング設定) +- 「マージ計算」Codeノード: 標準APIの生データ(`UserId`/`LoginId`/`DeptId`等)を、旧・模倣テーブルと同じキー名(`ユーザID`/`ログインID`/`組織`等)の中間形式へ変換する処理を先頭に追加。`組織`は`DeptId`から`プリザンターDepts取得`の結果で逆引き(`deptIdToName`マップ)。これにより後続のマージ判定ロジック(`pleasanterUserToFields`等)は無改造で流用できている +- `SecretKey`・`Manager`は本番データ上使用者ゼロを確認済み(該当者が出ても空文字扱いになるだけで実害小) + +### 5-2. ①の対応内容(production版のみ、2026-09-05完了) + +当初は「LWグループマッピング66件が本物のGroupsに1件も対応物を持たないため本物化には書込み移行が前提になり、構築のみでは完結しない」と判断していたが、①のワークフロー自体が元々「本物のGroupsに存在しないグループを自動で新規作成する」設計(差分計算→groupsToCreate→グループ作成実行)だったため、**ワークフロー定義の書き換え自体は構築の範囲で完結できる**と整理し直した(実際の新規作成・無効化という書込みは、このワークフローを実際に実行したときに初めて発生する=稼働テスト禁止と同じ制約の範囲内)。 + +- `模倣Groups取得`ノード → `Groups取得`へ改名、`items/504738/export` → `api/groups/get`(Offset+TotalCountページング設定込み) +- `グループ作成実行`ノード: `items/504738/create`(`ClassA`=独自連番, `ClassB`=グループ名) → `api/groups/create`(`GroupName`のみ送信。Pleasanterが自動採番するGroupIdを使う設計に変更、独自連番の`nextGroupId`計算は廃止) +- `グループ無効化更新実行`ノード: `items/{itemId}/update`(`CheckA`) → `api/groups/{groupId}/update`(body`{Disabled}`のみ、GroupIdはURLパス側)。**実機検証済み(2026-09-05、本人許可の下stagingのGroupId=40「テスト用グループ」で確認)**。当初`api/groups/update`(bodyにGroupId)で試したところ404(nginxレベルで存在しないパス)、`items`系と同じ「IDをURLパスに含める」パターンが正解だった +- `作成結果ペアリング`ノード: 独自連番をそのまま通していた処理から、`グループ作成実行`のレスポンス(`Id`)を実際に取得して使うよう変更 +- `差分計算`ノード: 標準APIから取得した本物のGroups一覧(`GroupName`→`GroupId`のマップ)を使い、LWグループマッピング66件の各行について「グループ名が本物のGroupsに実在するか」を追加でチェック。実在しなければ`groupsToCreate`に回して作り直し、実在するがグループID列がズレていれば`groupsToFixId`として修正対象に追加。これにより66件の移行問題もワークフロー実行時に自動的に解消される設計にした +- `新規マッピングCSV組立`ノード: `groupsToFixId`も統合。ここでCSVの列を共通ヘッダーにする際、fixId行が持たない列(分類・sourceKey等)を空文字で送ると既存値を上書き消去してしまう危険があった([[org-master-sync-n8n-mock-tables]]、8月の実データ事故と同種のパターン)ため、`groupsToFixId`側にも`category`/`sourceKey`/`groupName`の元の値を保持させて対応 + +**未検証・要注意点**: `グループ作成実行`のレスポンス形式(`Id`をトップレベルで返す前提、`createGroup`関数の実装から類推)、SplitInBatches(`グループ作成Loop`)内での入出力アイテムの対応順序、いずれも稼働テストなしでは確証が持てない。①のproduction版を初めて実行する際は、必ず少数件(1件)に絞ってから全体を回すこと。 + +(2026-09-05追記)`グループ更新`のURL(`/api/groups/{groupId}/update`)は本人許可の下stagingのGroupId=40「テスト用グループ」で実機検証済み。詳細は8-2節参照。 + +### 5-3. ④(統合版)の対応内容(production版のみ、2026-09-05完了) + +`NodeSrv/apps/org-master-sync/sample/sampleアカウント作成スクリプト.js`(本番プリザンターのフィールドスクリプトとして稼働実績あり)から、Users/Groups標準APIの正確な使用例が得られたため、これをベースに実装した。 + +- `プリザンターUsers取得`/`プリザンターGroups取得`/`プリザンターDepts取得`: ①③と同様に標準API化(`/api/users/get`, `/api/groups/get`, `/api/depts/get`、Pagination込み) +- `users_create実行`: `items/504737/create` → `/api/users/create`。サンプルスクリプトの`createPleasanterUser`関数のbody構造をそのまま採用(`LoginId`/`Name`/`Password`/`DeptId`/`DeptCode`/`MailAddresses`等、フラットなフィールド名でHashネスト不要) +- `users_update実行`: `items/{itemId}/update` → `/api/users/{userId}/update`。**このURL自体はサンプルスクリプトに例が無く実機未検証**。Groups update(`/api/groups/{groupId}/update`)と同じ「IDをURLパスに含める」パターンからの類推 +- `グループ取得`(メンバー判定用): `items/504738/export`(全件取得して対象1件をfind) → `/api/groups/{groupId}/get`(単体取得、対象1件だけ取得するよう効率化) +- `グループ更新実行`: `items/{itemId}/update` → `/api/groups/{groupId}/update`。サンプルスクリプトの`addUserGroup`関数を参考に、`GroupName`/`Body`/`GroupMembers`をまとめて送る形に変更(`Disabled`には触れない) +- **重要な発見**: 模倣Groupsの「グループメンバー(種別,キー,管理者フラグ 1行1メンバー)」というテキスト行形式は、本物の標準API`GroupMembers`配列の各要素(`'User,{userId},False'`という文字列)と**完全に同じフォーマット**だった(サンプルスクリプトで確認)。改行区切りテキスト⇔配列という違いだけで、行内容自体の変換は不要だった +- `対象判定`ロジックの変更点: + - 新規ユーザー作成時のメールアドレスを、旧実装の`社員マスタのメールアドレスのローカル部 + '@nexthd.jp'`固定ドメインから、**LINEWORKS側のメールアドレスをそのまま使う**方式に変更(本人指示、2026-09-05。ドメイン統一済みのため固定ドメイン付け替えは不要と判断) + - `DeptId`はUsers create時に必須のため、Depts標準APIの結果から`DeptName→DeptId`マップを新設(旧実装は`DeptCode`しか計算していなかった) + - 退職処理・組織変更の`userUpdateTasks.fields`を、模倣テーブルの内部列名(`無効`/`ロック`/`組織コード`等)から標準APIのフィールド名(`Disabled`/`Lockout`/`DeptCode`/`DeptId`/`MailAddresses`)へ変更 + +**未検証点**: `/api/users/{userId}/update`の実在(ダミーユーザー作成テストは本人許可が得られていないため確認できていない)。④のproduction版を実行する前に、退職処理か組織変更いずれかの経路で必ず1件だけ実行して確認すること。 + +### 5-4. ④-1・④-2・④-3の対応内容(production版のみ、2026-09-05完了) + +④(統合版)を機能別に分割した部分抜粋版のため、同じ修正パターンを適用した。 + +- ④-1新規ユーザー作成: ④と同じくUsers/Depts/Groups取得を標準API化、`users_create実行`を`/api/users/create`化。新規ユーザーの初期グループ所属反映(`書き戻しグループ反映組立`)もGroupMembers配列ベースに変更 +- ④-2ユーザー更新・退職処理: 同様にDepts/Users/Groups取得を標準API化。`対象展開`(退職処理・組織変更)を`/api/users/{userId}/update`化。模倣Usersテーブル固有の「itemId」概念(`userIdToItemId`によるUserId→模倣テーブル内部ID変換)は不要になった(標準APIはUserIdそのものが更新のキーになるため) +- ④-3グループ更新: Groups取得を標準API化、グループメンバー反映を`/api/groups/{groupId}/update`化 + +**重要な発見(バグ)**: ④(統合版)の対象判定コードに`const NEXT_GROUP_ALL_ID = '4'`という定義があったが、模倣Groupsテーブルにも本物のGroupsにも`GroupId=4`は存在しない誤った値だった(未使用の変数だったため実害はなかった)。④-1/④-2/④-3では同じ定数が正しく`'27'`(実在する「ネクストグループ全社員」グループ)になっていたため、そちらを正として全ワークフローで統一した。 + +**検証結果(2026-09-05追記)**: `/api/users/{userId}/update`は本人許可の下stagingのUserId=705「野木テストユーザ」で実機検証済み(`Disabled:false`送信、200 OK「"野木テストユーザ"を更新しました。」)。Groups/Depts系と同じ「IDをURLパスに含める」パターンで、④④④-1④-2の実装通りで問題なかった。①③④④-1④-2④-3、production版8本すべて未検証エンドポイントなしの状態になった。 + +## 6. 今回の制約(2026-09-05確認) + +- n8nワークフロー定義の編集(PUT/POST /workflows、Data Table操作含む)は確認不要で全自動 +- 本番プリザンター・LINEWORKSへの**読み取り**(GET系API、`items/{id}/export`、`items/{id}/get`含む)は許可 +- 本番プリザンターへの**書き込み**(create/update/import/bulkupsert等)は不可。特に①のグループ作成、③のマージ結果書き込みは明示的に禁止 +- ワークフローの稼働テスト(Webhookを実際に叩く、Manual Trigger実行)は不可