docs(org-master-sync): n8n production移行の設計メモを追加

模倣(Mock)Users/Depts/Groupsテーブルの設計意図と実態乖離、
staging/production分離、config値修正、①③④④-1④-2④-3の
標準API化(models/groups/{id}/update等の実機検証結果込み)を記録。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Kenichiro NOGI 2026-09-05 10:40:17 +09:00
parent 8e8bbcde6e
commit 5abf932fa6

View File

@ -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 GroupsGroupId実在範囲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 | 504408LINEWORKS:役職マスタ) |
| PLEASANTER_LEVEL_MASTER_SITE_ID_PROD | 504410LINEWORKS:職級マスタ) |
| PLEASANTER_USERTYPE_MASTER_SITE_ID_PROD | 504409LINEWORKS:利用権限マスタ) |
| PLEASANTER_BACKUP_SITE_ID_PROD | 504407社員・組織マスタ管理(副・バックアップ用) |
| PLEASANTER_LW_GROUP_MAP_SITE_ID_PROD | 504411LWグループマッピング、新規追加 |
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で試したところ404nginxレベルで存在しないパス、`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実行は不可