BlackSwan レセプト連携 基本設計書(Phase 1)
文書情報
| 項目 | 内容 |
|---|---|
| 文書名 | BlackSwan レセプト連携 基本設計書(Phase 1) |
| 版 | v1.3(2026-06-02 実装ステータス表記の整理=設計内容に集中) |
| 作成日 | 2026-05-30(v1.0)/ 2026-06-01(v1.1・v1.2)/ 2026-06-02(v1.3) |
| 対象 | BlackSwan Web ⇄ ケア樹(外部レセプトシステム)連携の Phase 1 本番稼動範囲 |
| 想定読者 | 開発者・PM・QA |
| 関連ドキュメント | 要件定義書 receipt-requirements.md / ギャップ調査 caretree-missing-fields-investigation.md / API マッピング caretree-api-mapping.md / 見積 estimate.md |
| 参照コード | packages/backend/src/modules/recept/(連携ドメイン)/packages/backend/src/modules/caretree/(汎用クライアント)/packages/backend/src/bff/web/recept-sync-page/(GraphQL)/packages/backend/schema.prisma(ReceptSyncJob ほか) |
1. 目的・適用範囲
1.1 目的
BlackSwan が保持する入所・契約・記録データを、外部の介護レセプト計算サービスである ケア樹 へ連携し、ケア樹側で介護給付費請求書・利用者負担請求書を生成できる状態にする。本設計書は Phase 1(〜2026-06 末・本番稼動)の本番リリース範囲を対象とする。
1.2 対象範囲(Phase 1)
| # | 機能 | 概要 |
|---|---|---|
| F1 | ケア樹 認証(事業所単位トークン) | token-only(PW 非保存)/契約共通 ID・PW は Corporation 保管/事業所ごとに token を BusinessOffice 保管 |
| F2 | 連携対象 API クライアント(9 系統+認証) | 各系統 POST / PUT |
| F3 | ETL(既存スキーマ → ケア樹形式 mapper) | 9 系統ごとに converter |
| F4 | 連携起動(Mutation)と非同期処理(ジョブキュー) | SQS(DLQ / リトライ / SQS マネージド暗号化)+ Strategy パターン + Recovery(cron 5 分) |
| F5 | プレビュー一覧画面 | 9 系統 × 利用者のセル マトリクス、ステータス別フィルタ、エラー詳細展開 |
| F6 | 事業所設定:ケア樹トークン取得セクション | Web UI 上で手動取得(無期限) |
| F7 | 部屋/部屋グループの連携データ(2026-05-29 決定) | Bed/Team への必須項目追加+対応表(bedId↔roomId・teamId↔roomGroupId)+発番 |
| F8 | 入退所・転室の連携 | F7 の対応表をキーに roomId を解決して連携 |
1.3 対象外(Phase 2 以降)
- 月初スケジューラ駆動の自動定期バッチ
- ケア樹 → BlackSwan の双方向同期(更新の取り込み)
- 差分検知(
/master/updatedAPI 活用) - 通所・ショート対応の事業所種別整合チェック
- 不足項目 7 項目(給付率・部屋等)の BlackSwan 入力 UI 拡張は Phase 2 概要に織り込み済(Go/No-Go 後日判断)
- 食事記録(04.02.02)/食事提供(02.04.08)/事業所食事提供(02.02.05)
1.4 連携対象 9 系統一覧
ReceptSyncType enum(schema.prisma:10879、ALL_SYNC_TYPES:recept-preview.types.ts:15)を正源とする。
| # | 系統 enum | ケア樹 API | 連携元 BS モデル | 備考 |
|---|---|---|---|---|
| 1 | USER_INFO(利用者情報) | 02.04.01 | User | 戻り値の riyoushaId を CaretreeUserMap に保存 |
| 2 | FAMILY_INFO(家族情報) | 02.04.02 | Family | 請求先フィールドは突合中(BS-4023) |
| 3 | INSURANCE(介護保険) | 02.04.04 | Insurance | youkaigoJoutaiKbnCd は careLevelId から変換 |
| 4 | SERVICE_PERIOD(サービス提供期間) | 02.04.03 | UserContract | serviceKyuufuShubetsuCd 給付種別 CD は要マッピング |
| 5 | ADMISSION(入退所) | 02.04.06 | BedAdmissionContract 等 | 看取り加算は任意(API 仕様) |
| 6 | MEAL_STOP(食止め) | 02.04.09 | ShiftReportMeal 等 | 開始⇔終了のペアリング ETL(BS-4125) |
| 7 | OUTING_OVERNIGHT(外出外泊) | 02.04.10 | OutingSchedule 等 | 終了系フィールド既存・ペアリング ETL |
| 8 | ROOM_TRANSFER(転室) | 02.04.07 | BedAdmissionContract | roomId は対応表で解決、tenshitsuTm は serviceStart 等から組立 |
| 9 | ROOM_BED(部屋・ベッド) | 02.02.04(部屋)/02.02.03(部屋グループ) | Bed/Team(5/29 決定:必須項目追加) | BS発番+1:1分割(多床室 101_1)。BS が居室費単価等を保持 |
要件定義書では一時「11 系統」と表記していたが、コード/schema は 9 系統が正源(部屋+部屋グループを
ROOM_BEDに統合、食事系はすべて対象外)。本設計書は 9 系統で記述する。🆕 v1.1 追記(2026-06-01):PR #1620 で
modules/caretree/sync-strategy.interface.tsに TypeScript 内部 enumSyncType(13 系統) が新規定義された(連携対象外のFACILITY_MEAL/MEAL_PROVISION/MEAL_RECORDも列挙)。これは Strategy パターン側で全 API 系統を網羅表現するもので、実際に Strategy が実装されるのは連携対象の 9 系統のみ。Prisma のReceptSyncType(9 系統)は連携結果の状態管理に引き続き使用。両 enum は段階的整理(将来 Prisma 側を TS 内部 enum 寄せ)の方向。
1.5 用語
| 用語 | 意味 |
|---|---|
| ケア樹 | Goodtree 社の介護レセプト計算サービス(外部システム) |
| 連携 | BlackSwan → ケア樹への一方向同期 |
| 系統 | 連携対象 API のグループ(上記 9 種類) |
| 連携ジョブ | ReceptSyncJob の 1 レコード(対象月 × 利用者 × 事業所 × 系統) |
| プレビュー | 連携ジョブの一覧(マトリクス)画面 |
| 対応表 | BlackSwan ID とケア樹採番 ID(integer)の対応関係を保持する内部テーブル |
| 1:1 分割 | BS のベッド 1 つ=ケア樹の部屋 1 つで対応させる方式(多床室は 101_1〜) |
2. システム全体像
2.1 アーキテクチャ
2.2 主要コンポーネントと責務
| コンポーネント | 場所 | 責務 |
|---|---|---|
recept-sync-page.resolver.ts | bff/web/recept-sync-page | GraphQL 入出力 |
ReceptService | modules/recept | 連携の中心ロジック(sync() / listPreview() / processJob() / markMaxRetriesExceeded()) |
ReceptSyncConsumerService | libs/sqs | SQS Consumer(sqs-consumer v15)。Business/Temporary エラー別ハンドリング |
ReceptSyncRecoveryService | modules/recept | Cron 5 分。PENDING(未エンキュー)と IN_PROGRESS(固着)を復旧 |
SyncStrategyRegistry | modules/caretree | ISyncStrategy 実装を SyncType で索引(登録順保持) |
ISyncStrategy(系統別実装) | modules/caretree/strategies/ | 1 系統の Extract → Transform → Load を担う Strategy |
ReceptSyncJobRepository | modules/recept | ReceptSyncJob の CRUD・一覧取得・Recovery 用検索 |
ReceptTokenProvider | modules/recept | ケア樹 API トークン供給 |
SqsModule / SQSClient プロバイダ | libs/sqs | SQSClient を NestJS DI に登録するグローバルモジュール |
CaretreeApiClient | modules/caretree | ケア樹 REST 汎用 HTTP(post/put)。Business/Temporary 例外を投げる |
CaretreeAuthClientService | modules/caretree | ケア樹 認証 /v1/token(取得・検証・削除) |
CaretreeBusinessError / CaretreeTemporaryError | modules/caretree/caretree-error.ts | 再試行不可(4xx 系)/再試行可(5xx 系)のエラー分類 |
| 対応表モデル | schema.prisma | BS ID ⇔ ケア樹採番 ID の対応 |
| FE プレビュー画面 | apps/blackswan-web/src/sections/recept-sync/ | プレビュー一覧/ジョブ詳細 |
| FE 事業所設定 トークン UI | 既存事業所設定ページに追加 | ケア樹トークン取得セクション |
| FE ベッド/チーム編集 | 既存ベッド/チームフォーム拡張 | 部屋必須項目入力(5/29 決定) |
2.3 データフロー(連携 1 件の例)
2.4 トランザクション境界
- 連携ジョブの起票(PENDING へ upsert):同期トランザクション。Mutation の即時応答に含める。
- 連携の実行:1 ジョブ=1 トランザクション。
IN_PROGRESSへの遷移 → ケア樹 API 呼出 → 対応表更新 →SUCCESS/ERRORへの遷移 を 1 単位に閉じる。ケア樹 API は外部呼出のため、DB 書込は API 成功後に行い、API 失敗時は対応表を更新しない(冪等性維持)。
3. データ設計
3.1 既存モデル(Phase 1 で参照)
| モデル | 役割 | 関係する系統 |
|---|---|---|
User | 利用者 | USER_INFO |
Family | 家族 | FAMILY_INFO |
Insurance | 介護保険情報 | INSURANCE |
UserContract | サービス提供期間 | SERVICE_PERIOD |
BedAdmissionContract | 入退所・ベッド割当 | ADMISSION/ROOM_TRANSFER |
Bed | ベッド | ROOM_BED(部屋情報も保持=5/29 決定)/転室時の対応表参照 |
Team | チーム | ROOM_BED(部屋グループ情報も保持=5/29 決定) |
BusinessOffice | 事業所 | ケア樹トークン保管先(要追加) |
Corporation | 法人 | マスタ ID/PW 保管先(要追加・連携時のみ使用) |
3.2 新規・拡張モデル
3.2.1 ReceptSyncJob
連携 1 件(対象月 × 利用者 × 事業所 × 系統)。
| フィールド | 型 | 説明 |
|---|---|---|
id | UUID v7 | PK |
targetYearMonth | String | 対象月(yyyy-mm) |
userId | UUID | 利用者(FK) |
businessOfficeId | UUID | 事業所(FK) |
syncType | ReceptSyncType | 9 系統のいずれか |
status | ReceptSyncStatus | PENDING/IN_PROGRESS/SUCCESS/ERROR |
executedByStaffId | UUID? | 実行者(バッチ時 null) |
executedAt | DateTime? | 最終実行日時 |
errorDetails | Json? | ケア樹レスポンス等のエラー本文 |
recoveryCount 🆕 | Int (default 0) | Recovery Service が IN_PROGRESS 固着ジョブを再試行した回数(無限ループ防止) |
corporationId | UUID | RLS(app.corporation_id 自動設定) |
| 制約 | @@unique([targetYearMonth, userId, businessOfficeId, syncType]) | 再連携は同一行更新 |
| インデックス | @@index([corporationId, targetYearMonth]) | プレビュー検索 |
| インデックス 🆕 | @@index([status, createdAt]) | Recovery Service の固着検出用 |
3.2.2 BusinessOffice 拡張
| 追加フィールド | 型 | 説明 |
|---|---|---|
caretreeToken | String? | ケア樹アクセストークン(暗号化保管) |
caretreeTokenAcquiredAt | DateTime? | トークン取得日時 |
caretreeJigyoushoId | String? | ケア樹側の事業所 ID(連携 path 用) |
3.2.3 Corporation 拡張
| 追加フィールド | 型 | 説明 |
|---|---|---|
caretreeMasterUserId | String? | ケア樹マスタ ID(連携時のみ使用・暗号化保管) |
caretreeMasterPassword | String? | ケア樹マスタ PW(PW 非保存方針との整合は 7.3 で確定) |
実装時は token-only に寄せ、PW を BS DB に永続化しない方式を最優先で検討する(操作画面で都度入力 → トークン取得後破棄/Secrets Manager 等)。
3.2.4 Bed 拡張(5/29 決定)
ケア樹 RoomModel の ★必須項目を Bed に追加し、Bed = 部屋(兼)として扱う。
| 追加フィールド | 型 | 説明 |
|---|---|---|
roomNo | Int | 部屋番号(ケア樹 0〜999) |
roomNm | String | 部屋名(多床室は 101_1 等) |
kai | Int | 階数 |
teiin | Int | 定員(1:1 分割では 1) |
type | String | 居室形態(CD) |
kyoshitsuType | String | 居室タイプ(CD:個室/多床室/ユニット 等) |
kyoshitsuhiTanka | Int | 居室費単価 |
startDt | Date | 開始日 |
sortNo | Int? | 並び順 |
3.2.5 Team 拡張(5/29 決定)
ケア樹 RoomGroupModel の ★必須項目を Team に追加し、Team = 部屋グループ(兼)として扱う。
| 追加フィールド | 型 | 説明 |
|---|---|---|
roomGroupNo | Int | 部屋グループ番号 |
sortNo | Int? | 並び順 |
既存の teamName/teamDisplayName をケア樹 roomGroupNm にマッピング。
3.2.6 対応表モデル
BS の内部 ID と ケア樹採番 ID(integer)を対応づける。
| モデル | キー | 用途 |
|---|---|---|
CaretreeBedRoomMap | bedId(FK)/caretreeRoomId Int/businessOfficeId | 転室 API の roomId 解決 |
CaretreeTeamRoomGroupMap | teamId(FK)/caretreeRoomGroupId Int/businessOfficeId | 部屋グループ参照 |
CaretreeUserMap | userId(FK)/caretreeRiyoushaId Int/businessOfficeId | path の riyoushaId 解決 |
CaretreeFamilyMap | familyId(FK)/caretreeKazokuId Int/businessOfficeId | 家族 API |
CaretreeInsuranceMap | insuranceId/caretreeKaigoHokenshouId/businessOfficeId | 介護保険 API(更新時) |
全モデルとも corporationId(RLS)・createdAt/updatedAt を持ち、@@unique([businessOfficeId, <bsId>]) で多重発番を防ぐ。
3.3 ER 関係(要約)
4. 機能設計
4.1 機能一覧
| ID | 機能 | 主要トリガ/呼出元 |
|---|---|---|
| FN-01 | 連携起動(プレビューから対象を選んで連携) | FE → Mutation startReceptSync |
| FN-02 | プレビュー一覧表示(9 系統 × 利用者) | FE → Query listReceptSyncPreview |
| FN-03 | エラー詳細表示(行クリック → アコーディオン展開) | FE のローカル展開(追加クエリなし) |
| FN-04 | ケア樹トークン取得(事業所単位) | FE → CaretreeAuthClientService.getToken |
| FN-05 | 9 系統 ETL(BS → ケア樹ペイロード変換) | ReceptService.sync 内部 |
| FN-06 | 対応表 ID 解決(bedId → caretreeRoomId 等) | ReceptService.sync 内部 |
| – | ||
| FN-08 | ダミー値発番(POST/PUT 双方の全項目送信) | ReceptService.sync 内部 |
| FN-09 | エラー記録(status=ERROR + errorDetails) | ReceptService.sync 内部 |
| FN-10 | ジョブ再連携(同一キーで status を更新) | FN-01 と同じ(@@unique キーで update) |
4.2 FN-01: 連携起動
入力:StartReceptSyncInput(recept-sync-page.type.ts:50)
| フィールド | 型 | 必須 | 既定値 |
|---|---|---|---|
userIds | [ID!]! | ✅ | – |
businessOfficeId | ID! | ✅ | – |
targetMonth | String!(yyyy-mm) | ✅ | – |
syncTypes | [ReceptSyncType!] | – | 未指定時は全 9 系統 |
処理(ReceptService.sync line 117〜):
ReceptSyncJobを upsert(status=PENDING)—@@unique([targetYearMonth, userId, businessOfficeId, syncType])で再連携は同一行更新。RECEPT_SYNC_REQUESTEDを emit(受付応答を即時返却)。- Listener が非同期で
sync()を実行。 sync()内:トークン取得 → Extract(既存 Repository 再利用)→ Transform(ETL)→ Load(ケア樹 API)→ 対応表更新 → status 更新。
4.3 FN-02: プレビュー一覧
入力:ListReceptSyncPreviewInput(recept-sync-page.type.ts:129)
| フィールド | 型 | 必須 | 既定値 |
|---|---|---|---|
businessOfficeId | ID! | ✅ | – |
targetMonth | String! | ✅ | – |
filter.status | ReceptSyncPreviewStatus? | – | 全件 |
filter.syncTypes | [ReceptSyncType!]? | – | 全 9 系統 |
sortBy | enum | – | USER_NAME_KANA_ASC |
pagination.offset / limit | Int | ✅ | – |
出力:ListReceptSyncPreviewOutput(recept-sync-page.type.ts:243)
summary:ステータス別件数(タブ件数表示用、母数=利用者数 × 9)totalCount:フィルタ後件数(ページャ用)users[]:(利用者 × 系統) の 1 セルを表す行。syncJob: nullの場合は未連携(NOT_SYNCED派生ステータス)。
ReceptSyncPreviewStatus enum(フィルタ専用):NOT_SYNCED/PENDING/IN_PROGRESS/SUCCESS/ERROR(recept-preview.types.ts:89)。
4.4 FN-05: ETL(9 系統 Strategy)
🆕 v1.1 反映(PR #1620):系統別の処理は Strategy パターンで実装する。各 Strategy は ISyncStrategy を実装し、SyncStrategyRegistry に DI 登録されることで ReceptService.processJob() から系統別呼出される。
// modules/caretree/sync-strategy.interface.ts
export interface ISyncStrategy {
readonly syncType: SyncType; // 13 系統の TypeScript 内部 enum
execute(context: SyncContext): Promise<void>;
}
export type SyncContext = {
receptSyncJobId: string;
userId: string;
businessOfficeId: string;
targetYearMonth: string; // "yyyy-mm"
token: string;
};
modules/caretree/strategies/(未配置)配下に系統ごとの Strategy を配置する。
| 系統 | Strategy クラス(提案) | 入力 | 出力 |
|---|---|---|---|
| USER | UserSyncStrategy | User + 対応表 | 利用者ペイロード POST/PUT |
| FAMILY | FamilySyncStrategy | Family | 家族ペイロード POST/PUT |
| INSURANCE | InsuranceSyncStrategy | Insurance | 介護保険ペイロード POST/PUT |
| SERVICE_PERIOD | ServicePeriodSyncStrategy | UserContract | サービス提供期間 POST/PUT |
| ADMISSION | AdmissionSyncStrategy | BedAdmissionContract | 入退所 POST/PUT |
| MEAL_STOP | MealStopSyncStrategy | 食止め記録 | 開始⇔終了をペア化 |
| ABSENCE | AbsenceSyncStrategy | 外出/帰所記録 | 開始⇔終了をペア化 |
| ROOM_TRANSFER | RoomTransferSyncStrategy | 同上+対応表 | roomId を対応表で解決・tenshitsuTm を組立 |
| ROOM / ROOM_GROUP | RoomSyncStrategy / RoomGroupSyncStrategy | Bed / Team | 5/29 決定の必須項目から組立 |
各 Strategy は副作用なしで Extract → Transform → Load を内包し、テスト時は対応する Repository / CaretreeApiClient をスタブで差し替える。
連携対象外の系統(
FACILITY_MEAL/MEAL_PROVISION/MEAL_RECORD)はSyncType列挙には含まれるが、対応する Strategy は実装しない(SyncStrategyRegistry.get()で未対応の連携種別例外)。
v1.0 では
modules/caretree/converters/に純粋関数(toRiyoushaPayload等)として実装する案だったが、PR #1620 で Strategy パターンに切り替え(Extract / Transform / Load を 1 つの Strategy に閉じ込めるため、依存と境界が明確化される)。
4.5 FN-06: 対応表による ID 解決(2026-06-01 改訂:BS発番+対応表のみ・名寄せ廃止)
- GET(名寄せ)は使わない。対応表に無ければ即 POST 発番。発番後の採番 ID を対応表に保存。
- 同じく
userId → caretreeRiyoushaId、teamId → caretreeRoomGroupId、familyId → caretreeKazokuIdを対応表で保持。
🆕 v1.2 変更点(PR #?? で別途反映):v1.1 までの「名寄せ(GET /master/room →
room.bikou優先 →bedName == roomNm)」フローは廃止した。理由は完全一方向化(GET 一切不使用)。代償としてケア樹側に手動登録された部屋との 重複リスクを受容する(Phase 1 運用前提:ケア樹マスタは BS 経由のみで管理)。
4.6 FN-07: GET → merge → PUT(2026-06-01 廃止)
🚫 本機能は採用しません。完全一方向(BS → ケア樹のみ) の方針で、PUT 更新時の GET 取得・merge は行わない。代わりに FN-08 のダミー値発番ルールに従い PUT も全項目送信(BS が持たない必須はダミー値で送る)。
影響:
- ケア樹側で
BS が知らないフィールドに値が入っていた場合、PUT で その値は上書きされる(partial-update は前提にしない)。- これを受容する条件として、ケア樹マスタ(部屋・部屋グループ等)は BS 経由のみで管理する運用前提を置く(手動編集禁止)。
- 共通ヘルパー
mergeAndPut<T>は実装しない(BS-4122 の対応範囲から外れる)。
4.7 FN-08: ダミー値発番(POST/PUT 双方に適用)
🆕 v1.2 改訂:完全一方向化(FN-07 廃止)に伴い、新規 POST だけでなく PUT 更新でも全項目送信する。BS が値を持たない ★必須項目はダミー値を使う(BS-4122)。
| 型 | 既定値 | 例 |
|---|---|---|
| integer(金額・番号・階数・定員) | 0 | kyoshitsuhiTanka=0 |
| string(名称) | "" | – |
| string(CD:項目種別) | 要 Goodtree 確認 | 「未設定」CD or 代表値 |
| date | 1900-01-01 または当日 | センチネル運用 |
| time | 00:00:00 | – |
5/29 決定の影響:部屋・部屋グループの ★必須項目は
Bed/Teamに追加され BS が実値を保持するため、本表の対象から外れる(残るのは住所カナ・CD 系・日付・時刻 など部屋系以外)。
4.8 FN-09: エラー処理
🆕 v1.1 反映(PR #1620):ケア樹 API エラーは modules/caretree/caretree-error.ts の 2 種類に明示分類して投げる。Consumer 側で分類に応じた処理を分岐させる。
| 例外クラス | 想定 HTTP | Consumer ハンドリング | ジョブ状態 |
|---|---|---|---|
CaretreeBusinessError | 4xx(必須不足・バリデーション・重複等) | 再試行せずメッセージ削除。errorDetails 保存 | ERROR(要画面からの再連携) |
CaretreeTemporaryError | 5xx・タイムアウト | VisibilityTimeout を指数バックオフで延長してキューに戻す。maxReceiveCount=5 で DLQ | ERROR(DLQ 滞留時) |
| 不正なメッセージ形式 | – | 再試行せずメッセージ削除 | – |
| BS 側例外(DB/コード不具合) | – | Sentry へ送信、errorDetails に概要保存 | ERROR |
| トークン失効 | 401/403 | CaretreeBusinessError 扱い。事業所設定画面で再取得を促す(自動再認証はしない) | ERROR |
最大試行回数到達時は
ReceptService.markMaxRetriesExceeded()を呼び出してメッセージを削除し、ジョブをERRORに確定させる(PR #1620・実装スタブのみ)。
4.10 Recovery(回復ポーラー)
🆕 v1.1 追記(PR #1620):ReceptSyncRecoveryService(modules/recept/recept-sync-recovery.service.ts)が NestJS Cron 5 分間隔で固着ジョブを回復する。
@Cron('*/5 * * * *')
async recoverStuckJobs(): Promise<void> {
await this.recoverPending(queueUrl); // 未エンキューの PENDING を再送
await this.recoverInProgress(queueUrl); // 古い IN_PROGRESS をリセット
}
| 対象 | 検出条件 | 復旧アクション | 無限ループ防止 |
|---|---|---|---|
| PENDING のまま再送漏れ | status=PENDING かつ作成から N 分経過 | SQS へ enqueue | – |
| IN_PROGRESS で固着 | status=IN_PROGRESS かつ最終更新から N 分経過 | status=PENDING に戻し再送 | recoveryCount をインクリメント。上限到達で ERROR に確定 |
検出には @@index([status, createdAt])(PR #1620 で追加)を利用。
4.9 FN-10: 再連携・冪等性(2026-06-01 改訂)
- ジョブの一意性:
ReceptSyncJobの@@uniqueキー。再連携は status/errorDetails/executedAt の更新で表現。 - ケア樹側:BS発番+対応表(FN-06) で多重発番を防ぐ。SQS at-least-once 配信下でも、対応表に既存なら PUT 経路へ分岐。
- 名寄せ(旧 GET ベース)は廃止したため、ケア樹側に手動登録された部屋との衝突は検知できない。運用前提:ケア樹マスタは BS 経由のみで管理する。
5. 画面設計
5.1 画面一覧
| 画面 ID | 画面名 | 場所 |
|---|---|---|
| SC-01 | レセプト連携 プレビュー一覧 | /recept/sync(新規) |
| SC-02 | 連携ジョブ詳細(エラー含む) | SC-01 のアコーディオン展開 |
| SC-03 | 事業所設定/ケア樹トークン取得 | 既存事業所設定ページに追加 |
| SC-04 | ベッド編集(部屋必須項目入力) | 既存ベッドフォーム拡張 |
| SC-05 | チーム編集(部屋グループ必須項目入力) | 既存チームフォーム拡張 |
詳細ワイヤーは
docs/receipt/wireframes/を参照。
5.2 SC-01:プレビュー一覧
主要要素:
- 対象月セレクタ(yyyy-mm、当月既定)
- 事業所セレクタ(権限のある事業所のみ)
- タブ:全件/未連携/連携待ち/連携中/成功/エラー(
summaryの各件数を表示) - 行:利用者 × 系統(9 系統横並びのセル マトリクス)
- セル:ステータスアイコン+実行日時。クリックで該当行アコーディオン展開(SC-02)
- 一括連携ボタン:チェックボックス選択行を
startReceptSyncへ送信 - 並び替え:利用者カナ昇/降順/実行日時降順
- ページャ:offset/limit
5.3 SC-02:ジョブ詳細
アコーディオン内:
- 該当ジョブの最終実行日時・実行者・エラー詳細 JSON
- 「再連携」ボタン(同一行を更新)
- ケア樹側採番 ID(対応表があれば表示)
5.4 SC-03:ケア樹トークン取得
- 事業所設定ページの 1 セクション
- マスタ ID/PW 入力欄(Corporation スコープ・連携時のみ使用・PW 非保存方針との整合は 7.3 参照)
- 「トークン取得」ボタン →
CaretreeAuthClientService.getToken()呼出 → 取得結果をBusinessOffice.caretreeTokenに暗号化保管 - 取得日時・トークン状態(未取得/取得済み)を表示
5.5 SC-04/SC-05:ベッド・チーム編集
5/29 決定の追加項目を入力可能にする:
- ベッド:部屋番号・部屋名・階数・定員・居室形態・居室タイプ・居室費単価・開始日
- チーム:部屋グループ番号・並び順
入力 UI のバリデーション(必須/数値範囲)は Zod スキーマで定義。bed-schema.ts の既存 roomNumber/bedNumber 層間ドリフトもこのタイミングで解消する。
6. 外部 IF 設計(ケア樹 REST API)
6.1 API ベース
| 環境 | URL |
|---|---|
| 本番 | https://api.caretree.jp |
| 開発/検証 | Goodtree 提供環境 |
getCaretreeApiBaseUrl()(caretree-env.ts)で環境変数 CARETREE_API_BASE_URL を解決。
6.2 認証 API
| メソッド | path | 用途 | 対応メソッド |
|---|---|---|---|
| POST | /v1/token | トークン取得(マスタ ID/PW から) | getToken() |
| GET | /v1/authorize | トークン検証 | verifyToken() |
| PUT | /v1/token | トークン再生成 | – |
| DELETE | /v1/token | トークン削除 | deleteToken()(運用上未使用予定) |
方針:事業所単位トークン・無期限・契約共通マスタ ID/PW。token-only(PW 非保存)。 取得は事業所設定画面で手動。失効時は再取得(再認証)し、自動の連携解除は実装しない。
6.3 業務 API(9 系統)
すべて事業所スコープ(/v1/jigyousho/{jigyoushoId}/...)。jigyoushoId は BusinessOffice.caretreeJigyoushoId から解決。
| 系統 | path(代表) | メソッド | 備考 |
|---|---|---|---|
| 利用者(02.04.01) | /v1/jigyousho/{jigyoushoId}/riyousha | POST/PUT/GET | 戻り値 riyoushaId を CaretreeUserMap に保存 |
| 家族(02.04.02) | /v1/jigyousho/{jigyoushoId}/kazoku | POST/PUT/GET | 同上 |
| 介護保険(02.04.04) | /v1/jigyousho/{jigyoushoId}/kaigo-hokenshou | POST/PUT/GET | |
| サービス提供期間(02.04.03) | /v1/jigyousho/{jigyoushoId}/teikyou-kikan | POST/PUT/GET | |
| 入退所(02.04.06) | /v1/jigyousho/{jigyoushoId}/nyuutaisho | POST/PUT/GET | |
| 食止め(02.04.09) | /v1/jigyousho/{jigyoushoId}/shokudome | POST/PUT/GET | ペアリング ETL |
| 外出外泊(02.04.10) | /v1/jigyousho/{jigyoushoId}/gaishutsu-gaihaku | POST/PUT/GET | ペアリング ETL |
| 転室(02.04.07) | /v1/jigyousho/{jigyoushoId}/riyousha/{riyoushaId}/tenshitsu | POST/PUT/GET | path に riyoushaId 必須・roomId 必須・tenshitsuTm 必須 |
| 部屋(02.02.04) | /v1/jigyousho/{jigyoushoId}/master/room | POST/PUT/GET | 戻り値 roomId を対応表に保存 |
| 部屋グループ(02.02.03) | /v1/jigyousho/{jigyoushoId}/master/room-group | POST/PUT/GET | 戻り値 roomGroupId を対応表に保存 |
詳細パス・パラメータは
docs/receipt/caretree-api-v1.swagger.json、マッピングはcaretree-api-mapping.mdを正とする。
6.4 共通リクエスト/レスポンス
- 認証:
Authorization: Bearer <token>(CaretreeApiClient.authHeader参照) - Content-Type:
application/json - 文字コード:UTF-8
- タイムアウト:5000ms(
CaretreeModuleのHttpModule設定) - エラーレスポンス:4xx/5xx の本文を
ReceptSyncJob.errorDetailsに保存
7. 共通機能・非機能
7.1 ロギング
nestjs-pinoを全モジュールで使用ReceptServiceはcorporationId/businessOfficeId/targetMonth/userIdCount/syncTypeCountを構造化フィールドで出力- 連携 1 件ごとに開始/成功/失敗ログを残す(CloudWatch Logs 経由で検索)
7.2 エラー監視
@sentry/node。Listener で try-catch して未処理 Promise reject を防止(プロセス停止回避)- Sentry の breadcrumb で連携対象月・ジョブ ID を残す
7.3 セキュリティ
| 観点 | 方針 |
|---|---|
| トークン保管 | DB 列レベル暗号化(AES-256)。BusinessOffice.caretreeToken 列に保存し、復号は ReceptTokenProvider に閉じる |
| マスタ ID/PW | PW 非保存方針を最優先。実装案:(A) 操作時のみ画面入力 → トークン取得後破棄、(B) Secrets Manager 経由で取得時のみ復号、(C) 暗号化保管+アクセス制限。決定は Phase 1 早期に確定(BS-4107) |
| RLS | 全モデルで corporationId の RLS(current_setting('app.corporation_id')) |
| 通信 | HTTPS(TLS 1.2 以上) |
| 認可 | プレビュー一覧/連携起動は施設管理者ロールに限定(FE/BFF 双方でチェック) |
| 監査 | ReceptSyncJob.executedByStaffId に実行者を記録 |
7.4 ジョブキュー
| 項目 | 設定/実装 |
|---|---|
| キュー | ${env}-billing-sync-queue(SQS 標準キュー)/${env}-billing-sync-dlq(DLQ) |
| 暗号化 | SQS マネージド暗号化(メイン・DLQ 共に QueueEncryption.SQS_MANAGED) |
| visibility timeout | 300 秒(5 分) |
| retention | メイン 1 日/DLQ 14 日 |
| maxReceiveCount | 5(5 回失敗で DLQ) |
| Consumer | ReceptSyncConsumerService(libs/sqs/、sqs-consumer v15) |
| エラー分類 | CaretreeBusinessError(再試行せず削除)/CaretreeTemporaryError(指数バックオフで再キュー) |
| 回復ポーラー | ReceptSyncRecoveryService(cron 5 分、PENDING/IN_PROGRESS 復旧、recoveryCount でループ防止) |
| インフラ定義 | packages/infra/lib/billing-sync-queue.ts(BillingSyncQueue Construct) |
| NestJS 配線 | libs/sqs/sqs.module.ts(グローバル)/ libs/sqs/sqs-client.provider.ts(SQSClient DI) |
EventEmitter は段階的削除対象:
recept-sync.listener.tsとrecept-sync.event.tsは残存しており、Strategy 実装と並行して削除予定。
設計の詳細は PR #1620 で追加された ADR / 設計ドキュメントを参照:
docs/backend/caretree/ADR-001-job-queue-selection.mddocs/backend/caretree/bs-3970-implementation-plan.mddocs/backend/caretree/bs-3970-infra-update.mddocs/backend/caretree/nestjs-integration-design.md
7.5 冪等性
- ジョブの一意性:
ReceptSyncJob @@unique - ケア樹側:名寄せ+bikou 相互参照(FN-06)+対応表で多重発番を防ぐ
- SQS at-least-once 配信下でも、対応表の存在確認 → PUT 経路に切替で安全
7.6 性能・可用性
| 観点 | 要件 |
|---|---|
| 同時実行数 | 1 事業所あたり 1 並列(ケア樹 API のレート制限考慮・要確認) |
| 1 件当たり処理時間 | 1〜3 秒(ケア樹 API レスポンス依存) |
| 1 月次連携の総時間 | 1 事業所 100 利用者 × 9 系統 = 900 ジョブ → 約 30〜60 分(並列度 4 想定) |
| 可用性 | BlackSwan SLO に準拠(99.5%) |
| 復旧 | SQS DLQ にあるジョブは運用画面から再連携可能 |
| データ保持 | ReceptSyncJob は 12 か月保管 → 以降アーカイブ(運用合意要) |
| 監視 | プレビュー一覧の ERROR 件数を CloudWatch メトリクス化(運用画面に件数表示) |
| 障害通知 | DLQ 投入時に Slack 通知(運用チャンネル) |
| 性能試験 | Phase 1 リリース前に 1 事業所 100 利用者 × 9 系統で実測 |
8. 移行(Phase 1 リリース時)
| # | 作業 | 担当 | タイミング |
|---|---|---|---|
| M1 | BusinessOffice/Corporation への新規列追加マイグレーション | BE | リリース前 |
| M2 | Bed/Team への部屋必須項目追加マイグレーション | BE | 同上(既存行は NULL/既定値で初期化) |
| M3 | 対応表モデル追加マイグレーション | BE | 同上 |
| M4 | ReceptSyncJob は既存(追加マイグレーション不要) | – | – |
| M5 | 初回ケア樹トークン取得(事業所単位) | 施設管理者 | 本番切替直後 |
| M6 | 既存ベッドの部屋情報初回入力(または ケア樹からの取り込み) | 施設管理者 | 本番切替後 1〜2 日以内 |
| M7 | 対応表の初回ブートストラップ(既存ベッド → ケア樹発番) | BE バッチ | M6 完了後 |
9. 制約・前提・残課題
9.1 前提
- ケア樹本番 URL:
https://api.caretree.jp(Goodtree 確定) - ケア樹トークンは無期限(取得時点の手動運用)
- BS 認証ロールは施設管理者のみ操作可能
9.2 残決定(次回確定要)
| # | 項目 | 概要 |
|---|---|---|
| 2026-06-01 確定:BS で入力(完全一方向=ケア樹取り込み不可。実値は BS 側で管理) | ||
| Q2 | マスタ ID/PW の保管方式 | 7.3 セキュリティの A/B/C 案から選択 |
| Q3 | BS-3914(部屋非連携 Done)の顧客再合意 | 5/29 方針で覆る前提 |
| Q4 | POST /master/room の実必須項目(Goodtree 確認) | ダミー値の確定に必要 |
| Q5 | teiin=1 のレセプト金額影響(Goodtree 確認) | 多床室 1:1 分割の妥当性検証 |
| Q6 | CD 系の「未設定」CD 有無(Goodtree 確認) | FN-08 ダミー値の確定に必要 |
| Q7 | フェーズ配置(部屋/部屋グループ・転室 ≈ 46h) | Phase 1 本体に繰入か/別フェーズか |
9.3 経緯(参考)
- 2026-05-12:同期のみ方針(BS-3886)
- 2026-05-19:認証は事業所単位トークン(BS-4061)
- 2026-05-22:給付率(負担率変換)、家族連携、食事提供系を整理(11 系統)
- 2026-05-25:転室・部屋連携の方式を BS発番+1:1分割で確定(BS-4098)
- 2026-05-27:シューペルブリアン様定例で部屋連携方式の再議論(案 B 提案)
- 2026-05-29:部屋・部屋グループは
Bed/Teamへ必須項目追加(新モデルなし)で確定(案 C 4 概念分離・案 B マッピング UI ともに不採用) - 2026-05-29:PR #1620 で SQS 基盤・Strategy パターン・Recovery Service を実装(BS-3970)。EventEmitter walking skeleton から SQS Consumer へ移行(旧 listener は段階的削除)
- 2026-06-01:本基本設計書を v1.1 に改訂し PR #1620 実装を反映(PR #1664 で v1.0 初版、PR #1665 で v1.1)
- 2026-06-01:完全一方向化を確定(v1.2)。GET 一切不使用(名寄せ廃止)・BS発番+対応表のみ・PUT 全項目送信(ダミー含む)。FN-07 GET-merge-PUT 廃止、Q1(実値の入力主体)は BS 入力で確定。ケア樹マスタは BS 経由のみで管理する運用前提。
付録 A. 参照ファイル一覧
| 区分 | ファイル | 行数 |
|---|---|---|
| BE モジュール | packages/backend/src/modules/recept/recept.module.ts | 27 |
| BE | packages/backend/src/modules/recept/recept.service.ts | 498 |
| BE | packages/backend/src/modules/recept/recept-sync-job.repository.ts | 220 |
| BE | packages/backend/src/modules/recept/recept-preview.types.ts | 169 |
| BE | packages/backend/src/modules/recept/recept-sync.event.ts | 31 |
| BE | packages/backend/src/modules/recept/recept-sync.listener.ts | 53 |
| BE | packages/backend/src/modules/recept/recept-sync.mock.ts | 51 |
| BE | packages/backend/src/modules/recept/recept-token.provider.ts | 33 |
| BE | packages/backend/src/modules/caretree/caretree.module.ts | – |
| BE | packages/backend/src/modules/caretree/caretree-api.client.ts | – |
| BE | packages/backend/src/modules/caretree/caretree-auth-client.service.ts | 72 |
| BE | packages/backend/src/modules/caretree/caretree-env.ts | 16 |
| BE 🆕 | packages/backend/src/modules/caretree/caretree-error.ts | 17 |
| BE 🆕 | packages/backend/src/modules/caretree/sync-strategy.interface.ts | 34 |
| BE 🆕 | packages/backend/src/modules/caretree/sync-strategy.registry.ts | 28 |
| BE 🆕 | packages/backend/src/modules/recept/recept-sync-recovery.service.ts | 116 |
| BE 🆕 | packages/backend/src/libs/sqs/recept-sync-consumer.service.ts | 142 |
| BE 🆕 | packages/backend/src/libs/sqs/sqs-client.provider.ts | 12 |
| BE 🆕 | packages/backend/src/libs/sqs/sqs.module.ts | 10 |
| Infra 🆕 | packages/infra/lib/billing-sync-queue.ts | 40 |
| Migration 🆕 | packages/backend/migrations/20260523000000_add_recovery_count_to_recept_sync_job/migration.sql | – |
| 設計 ADR 🆕 | docs/backend/caretree/ADR-001-job-queue-selection.md | – |
| 設計 🆕 | docs/backend/caretree/bs-3970-implementation-plan.md | – |
| 設計 🆕 | docs/backend/caretree/bs-3970-infra-update.md | – |
| 設計 🆕 | docs/backend/caretree/nestjs-integration-design.md | – |
| BFF | packages/backend/src/bff/web/recept-sync-page/recept-sync-page.resolver.ts | – |
| BFF | packages/backend/src/bff/web/recept-sync-page/recept-sync-page.type.ts | 255 |
| BFF | packages/backend/src/bff/web/recept-sync-page/recept-sync-page.module.ts | – |
| BFF | packages/backend/src/bff/web/recept-sync-page/recept-sync-page.e2e-spec.ts | – |
| Schema | packages/backend/schema.prisma:10829-10889(ReceptSyncJob + enum) | – |