Notificationテーブルのスキーマ変更#22
Conversation
Notification の targetUserIds/isNotified を targetUsers (NotificationTargetUser) に置き換えた openapi.yaml に合わせて、oapi-codegen で生成した API 型を再生成する。
API スキーマ変更に追従し、ドメインの TargetUserIDs を NotificationTargetUser のスライスへ変換するようにする。IsNotified フィールドは API から削除されたため出力しない。
OpenAPI スキーマで Notification.isNotified を廃止し targetUsers[].notifiedAt で通知日時を管理する形へ変更されたため、 GORM モデル・ドメイン・リポジトリ・サービス・コンバータを追従させた。 - database.Notification の IsNotified カラムを削除し、 database.NotificationTargetUser に NotifiedAt を追加 - domain.Notification の IsNotified / TargetUserIDs を廃止し、 UserID と NotifiedAt を保持する domain.NotificationTargetUser を導入 - DispatchNotifications を通知ID→送信成功UserID のマップ受け取りに変更し、 notification_target_users.notified_at を該当ユーザーに対して更新 - サービス層は未通知ユーザー(NotifiedAt が nil)のみを送信対象にし、 送信成功したユーザーだけを per-notification で通知済みに記録 - ListNotifications の isNotified フィルタは notification_target_users の notified_at に対する EXISTS 句で表現 - UpdateNotification では既存行の notified_at を維持したまま 対象ユーザー集合を入れ替え - handler.toAPINotification / toDomainNotification を新スキーマに合わせて変換
従来は対象ユーザーのいずれか宛にトークンが存在すれば、FCM 送信結果に関わらず対象ユーザー全員を配信済みとして記録していた。 本コミットでは、トークンとユーザー ID の対応を保持し、SendEachForMulticast の応答からユーザー単位の成功可否を判定して、実際に配信に成功したユーザーのみを deliveries に積むよう変更した。 併せて FCM 呼び出し自体がエラーになった場合でも、その時点までに成功したユーザー分は確定保存する挙動に揃えている。
There was a problem hiding this comment.
Pull request overview
Notification の通知済み状態を Notification.isNotified から対象ユーザー単位の NotificationTargetUser.notifiedAt へ移行し、送信成功ユーザーのみを通知済みとして記録できるようにする変更です。
Changes:
- ドメイン/DBモデルを
TargetUsers+NotifiedAtへ移行(IsNotified廃止) - ディスパッチ処理を「通知ID → 成功ユーザーID配列」の更新方式に変更
- 一覧フィルタの
isNotifiedをnotification_target_users.notified_atの状態で判定するSQLへ変更
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/service/notification_dispatch.go | 未通知ユーザーのみ送信対象にし、成功ユーザー単位で配達結果を収集 |
| internal/service/notification.go | リポジトリIFを deliveries map[notificationID][]userID 受け取りに変更 |
| internal/repository/util.go | NotificationTargetUser のユーザー重複排除ユーティリティ追加 |
| internal/repository/notification_update.go | ターゲットユーザー差し替え時に既存 notified_at を保持して再作成 |
| internal/repository/notification_list.go | isNotified フィルタを notified_at の EXISTS 条件で表現 |
| internal/repository/notification_dispatch.go | 送信成功ユーザーの notified_at を更新する実装へ変更 |
| internal/repository/notification_create.go | 作成時に TargetUsers をDBへ保存(NotifiedAt 含む) |
| internal/handler/converter.go | API⇄ドメイン変換を targetUsers(レスポンス)へ追従 |
| internal/domain/notification.go | IsNotified/TargetUserIDs 廃止、TargetUsers[] 導入 |
| internal/database/notification_target_user.go | notification_target_users.notified_at カラム追加 |
| internal/database/notification.go | is_notified カラム削除、ToDomain/FromDomain を TargetUsers 対応 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
masaya-osuga
left a comment
There was a problem hiding this comment.
Copilotの新しいレビューも妥当そうな気がする
多分修正した |
Dispatch 対象に FCM トークン保持ユーザーと未登録ユーザーが混在する場合、 従来は送信成功ユーザーのみ deliveries に積んでいたため、未登録ユーザーの notified_at が更新されず再ディスパッチされ続けていた。送信成功ユーザー に加えてトークン未登録ユーザーも delivered に含めるよう修正した。
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
masaya-osuga
left a comment
There was a problem hiding this comment.
Copilotの指摘直してもいいと思うけど、ほぼ問題ないと思うのでApproveします
ごめんなさい、いま慌てて直してました |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
UNNEST + JOIN 化した実装を取りやめ、通知IDごとに UPDATE notification_target_users ... WHERE notification_id = ? AND user_id IN ? を発行する素直な実装に差し戻した。RowsAffected を見て 更新が発生した通知IDのみ後続の取得対象に含める挙動は維持する。
OpenAPI 上 /v1/notifications/dispatch は「送信状態に関わらずまとめて送信する」 仕様であり、再ディスパッチ時にも notified_at を最新の送信時刻で更新する必要がある。 そのため UPDATE 条件から notified_at IS NULL を外し、対象ユーザーの行を 無条件に now で更新するようにした。
OpenAPI の /v1/notifications/dispatch は送信状態に関わらず指定 ID の通知を まとめて再送する仕様であるため、サービス層で NotifiedAt != nil の TargetUser を スキップしている分岐を取り除き、全ターゲットユーザーを FCM 送信対象にする。 notified_at の更新はリポジトリ側で全ユーザーを上書きする実装に揃える。
DispatchNotifications をユニットテストで検証できるようにするため、 *messaging.Client への直接依存を MessagingClient interface (SendEachForMulticast のみ) に差し替える。 *messaging.Client は引き続きこの interface を満たすため呼び出し側に変更はない。
通知ID指定からの送信フロー全体をスタブで検証する。 - 対象通知が存在しない / TargetUsers が空のときは FCM もリポジトリも呼ばない - FCM トークン未登録ユーザーや既通知ユーザーも deliveries に含めて再送扱いにする - FCM 部分成功時は成功ユーザーのみ delivered として記録する - バッチ全体エラー時は dispatch を呼ばずエラーを握り潰してログのみ出す - リポジトリ/FCMトークン取得のエラーはそのまま伝搬する
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
概要
Notification の通知済み状態を、Notification 単位の
isNotifiedフラグから対象ユーザー単位のnotifiedAt(NotificationTargetUser.notifiedAt) へ移行する。これに伴い OpenAPI スキーマ・生成コード・GORM モデル・ドメイン・リポジトリ・サービス・コンバータを一括で追従させる。背景
isNotifiedを持っていたため、対象ユーザーごとの通知成功/未送信状態を表現できなかった。Notification.targetUserIds/Notification.isNotifiedを廃止し、Notification.targetUsers(NotificationTargetUser { userId, notifiedAt }) で対象ユーザーと通知日時を管理する形に変更されたため、user-api 側を追従させる必要がある。スキーマ仕様メモ
dotto-typespec の仕様に合わせ、リクエストとレスポンスでフィールド形が異なる点に注意:
Notification):targetUsers: NotificationTargetUser[](userIdとnotifiedAtを保持)NotificationRequest):targetUserIds: string[](作成・更新時はユーザーID配列のみ受け取り、notifiedAtはサーバー側で管理)変更点
OpenAPI / 生成コード
openapi/openapi.yamlを最新の dotto-typespec 出力に更新し、レスポンスをtargetUsers(NotificationTargetUser) 形式へ移行(リクエストはtargetUserIdsのまま)。generated/api.gen.goを oapi-codegen で再生成。データベース層 (
internal/database)database.NotificationからIsNotifiedカラムを削除。database.NotificationTargetUserにNotifiedAt *time.Timeを追加し、対象ユーザーごとに通知日時を保持する。ドメイン層 (
internal/domain)domain.NotificationのIsNotified/TargetUserIDsを廃止。UserIDとNotifiedAtを保持するdomain.NotificationTargetUserを導入し、Notification.TargetUsersとして扱う。リポジトリ層 (
internal/repository)DispatchNotificationsのシグネチャを「通知ID → 通知済みとして記録する UserID 集合」のマップ受け取りへ変更し、notification_target_users.notified_atを該当ユーザーに対してのみ更新するよう修正。ListNotificationsのisNotifiedフィルタを、notification_target_users.notified_atに対する EXISTS 句で表現。UpdateNotificationで対象ユーザー集合を入れ替える際、既存行のnotified_atを保持したまま差分更新するよう修正。internal/repository/util.goに追加。サービス層 (
internal/service)notification_dispatchで未通知ユーザー (NotifiedAtが nil) のみを送信対象として抽出。notifiedAtを埋めて通知済み扱いとする(送信不能ユーザーの状態確定)。ハンドラ層 (
internal/handler)toAPINotificationをレスポンスのtargetUsers形式に合わせて変換。toDomainNotificationをリクエストのtargetUserIdsからdomain.NotificationTargetUserを構築するよう修正。確認事項