Fix: Pro機能Epicリリース前レビュー指摘の解消(Critical+Major) - #326
Conversation
Pro機能Epicリリース前レビュー(issue #445)で判明した、admin向け強制Proモード(PRD-01 US-08)の未実装を解消。 users.is_adminを追加し、Subscription#pro_active?でdevelopment環境かつadminなら常にtrueを返すようにする。 rails pro:make_admin[email]でdevelopment限定にis_adminを付与できる。
update_paramsの許可リストに残っていたため、作成後に指標を差し替えられてしまい フロント(mobile new.tsx)の「作成後は指標変更不可」という前提と矛盾していた。 指標を差し替えると既存のtarget_value/manual_current_valueが無意味になるため、 period_type/season_idと同様に更新不可にする。
granularity=season はシーズン跨ぎで全シーズンを比較する機能だが、既存の season_id フィルタが同時に渡されると単一シーズンに絞り込まれてしまい、 比較の意味が失われていた。season粒度選択時はseason_idを無視するようにする。
これまではlast_synced_atを更新するだけのスタブだったため、Webhookの取りこぼし・
配信遅延時に「同期更新」ボタンが実質何もしていなかった。RevenueCat::SubscriberClient
でGET /v1/subscribers/{app_user_id}を叩き、RevenueCat::SubscriberSyncで
entitlements/subscriptionsの現在状態(expires_date・grace_period・billing_issue・
unsubscribe等)をSubscriptionへ反映する。単発の状態確定処理のため、Webhook Handler
と違い通知Jobは発火しない。
強制Proモード実装で追加したusers.is_adminが、v1のUsersController#show_user_id_data (他ユーザーのプロフィール閲覧)と#search(ユーザー検索)でas_json経由でそのまま レスポンスに含まれてしまっていた。内部向けの開発用フラグを他ユーザーからも 参照できてしまう状態だったため、except: :is_adminで除外する。 #show(自分自身の情報取得)も同様に除外し、旧クライアント向けv1 API形状の golden snapshotを変更しない。
F-16(バックグラウンド継続実行)がPro差別化要素としてPRDに明記されているが 対応するentitlementキー自体が存在しなかった。shadow_swing_backgroundを追加する。 実装はmobile側(counter.tsx)。
/api/v2/plans/calendar が entitlement を一切見ておらず、無料ユーザーも 全期間を閲覧できてしまっていた。schedule_calendar_full_history entitlementを 追加し、無料は当日の前後15日(直近月中心)にfrom/toをクランプする。
PRD-06で投手側も打者側と同様のシーズン跨ぎ比較・自己ベスト強調が求められていたが
月別集計のみでgranularity自体が存在しなかった。EraTrendServiceにseason粒度を追加し
season_transition_graph entitlementで打者側と同じくPro限定にする。season粒度選択時の
season_idクランプもbatting_trendと共通化(season_aware_params)した。
レスポンス形状を{trend: [{month, era}]}から{granularity, points: [{key, label, era}]}へ
統一し、batting_trendと同じ形にした。
FinalizeGoalsJobがgoal_badgesを作成する一方、閲覧できるAPI・UIが存在せず DoDの「バッジ獲得が動作する」が実質未達だった。GET /api/v2/goal_badgesで 自分のバッジを新しい順に返す(閲覧のみ、付与は引き続きFinalizeGoalsJobが担当)。
status判定はgrace_period_expires_dateを考慮したeffective_expires_atを使う一方、 expires_atカラムには生のentitlement['expires_date']を保存していたため、 グレース期間中に同期するとstatusは'active'/'billing_issue'のままexpires_atだけ 過去日付になり、Subscription#pro_active?がfalseを返してPro機能が失われていた。 Webhook Handler群がグレース中にexpires_atを変更しない設計と揃え、 effective_expires_atを保存するようにする。
Net::HTTP.startのタイムアウト・接続断・TLSエラーがRequestFailedErrorへ 変換されておらず、SyncControllerのrescueをすり抜けて素の500になっていた。 想定される主要な失敗モードをRequestFailedErrorへ変換し、意図通りbad_gatewayに 倒れるようにする。open_timeoutも明示的に設定した。
This reverts commit 1296373.
This reverts commit f005872.
前後15日は狭すぎるとの判断で、今日を中心に前後3ヶ月(月単位)へ広げる。 月末月初での日数ズレを避けるためActiveSupportの3.monthsで計算する。
is_adminカラムのマイグレーションrevert時にdb:schema:loadを実行し、 開発環境の全データ(ユーザー・Flipperフラグ設定等)を消してしまった実例を踏まえ、 特定マイグレーションのみを戻すdb:rollback/db:migrate:downを使うこと、 データ削除を伴う操作は事前に確認することを明記する。
試合記録のみで練習を一度もしていないユーザーで、以下2つの独立した問題により 実際には存在しない「傾向」が断定カードとして表示されていた。 1. 素振り本数のような入力値が全週0(変動が無い)場合でも上位群/下位群に 分割してしまい、意味の無い差分をあたかも傾向であるかのように表示していた。 入力値に変動が無いペアは非断定カードにする。 2. 「練習した日数」の判定にintensity_level(草・Streak向けの定義で試合のみの日も L4として含む)を使っていたため、試合しか記録していない日も「練習した日」として カウントされていた。practice_menu_countまたはtotal_swing_countが実際に 正の値を持つ日だけを練習日として数えるようにする。
|
@claude |
|
Claude finished @ippei-shimizu's task in 4m 55s —— View job コードレビューTODOリスト
全体評価Critical/Major項目(goal指標変更不可・シーズン粒度のseason_id縮退バグ・RevenueCat実同期・カレンダーPro出し分け・ERA推移拡張・goal_badges閲覧API)はいずれも実装・テストとも妥当です。セルフレビューでグレース期間中の Minor指摘
良い点
テストについて
|
…合を修正 Webhook側のBaseHandler#unknown_product?は未登録のproduct_id/storeが来ると 更新自体をスキップしSentryへ警告するが、SubscriberSyncには同等のガードが無く、 plan_type/platformにnilを静かに保存していた。RevenueCat側に新SKUが追加されて PlanCatalogの追従が漏れた場合、Webhook経由では検知できてもsync経由では 気付けない非対称が生まれていたため、同じガードを追加する。
現状write_timeoutオプションを渡していないため実害は無いが、将来 write_timeoutを設定した際にこのリストへの追加が漏れやすいため先に含めておく。
Webhook側のENV.fetch('REVENUECAT_WEBHOOK_SECRET', nil)と違いデフォルト値が
無く未設定時はKeyErrorになる点についてレビューで確認を求められたため、
意図的な設計判断であることをコメントで残す。
概要
issue #445(Pro機能Epicリリース前レビュー指摘の解消)のうち、back側のCritical/Major項目に対応する。
対応内容
metric_key/comparison_type/practice_menu_idを変更不可にする修正(指標差し替えで既存target_valueが無意味になる不整合を防止)granularity=season)でseason_id絞り込みと併用すると1シーズンに縮退する不具合を修正(batting_trend/era_trend共通処理として抽出)/pro/syncをRevenueCat REST APIとの実同期に置き換え(RevenueCat::SubscriberClient/RevenueCat::SubscriberSyncを新規追加。グレース期間・タイムアウト等のエッジケースはセルフレビューで追加修正済み)shadow_swing_background。実装はmobile側)GET /api/v2/plans/calendar)にPro/無料の閲覧範囲出し分けを追加(schedule_calendar_full_historyentitlement、無料は今日の前後3ヶ月にクランプ)EraTrendChart)にシーズン粒度・自己ベスト強調を追加(era_trend_serviceを月/シーズン対応に書き換え、レスポンス形状を{trend:[{month,era}]}から{granularity,points:[{key,label,era}]}へ変更。batting_trendと統一)goal_badges)を閲覧するAPIを追加(GET /api/v2/goal_badges)セルフレビュー(自動実施)で追加修正した項目
expires_atへ生の期限を保存しpro_active?を壊していた不具合(SubscriberSync)SubscriberClient)外部レビュー対応で追加した項目
SubscriberSyncがPlanCatalog未登録のproduct_id/storeを無視してplan_type/platformにnilを静かに保存していた不具合を修正(Webhook側のunknown_product?と同じガード・Sentry警告を追加)NETWORK_ERRORSにNet::WriteTimeoutを追加(現状write_timeout未設定のため実害なし、将来設定時の考慮漏れ防止)secret_keyのENV.fetchにデフォルト値を設けていない意図(設定ミスは502で揉み消さずKeyErrorで気付けるようにする)をコメントで明記スコープ外(別issueへ切り出し)
以下2件はバックエンドの新規ジョブ・push基盤など大規模な追加実装が必要なため、別issueで対応する:
またissue #445のMinor項目(32件)は今回のスコープから除外し、別PRで自動対応予定。
スコープから外した項目
development環境のadminユーザーに強制Proモードを実装する対応は、不要と判断し取り下げ(
git revert)。手動テストで見つかった不具合の修正
カレンダー俯瞰の閲覧範囲を無料は前後15日→前後3ヶ月に変更(狭すぎるとの判断)。
「練習と成績のつながり」(相関インサイト)で、練習を一度もしていない(試合記録のみの)ユーザーにも
実在しない「傾向」が断定表示される不具合を修正。以下2つの独立した問題が原因だった:
傾向として表示していた。入力値に変動が無いペアは非断定カードにする
intensity_levelを使っていたため、試合しか記録していない日も「練習した日」としてカウントされていた。
practice_menu_countまたはtotal_swing_countが実際に正の値を持つ日だけを練習日として数えるようにするテスト
docker compose exec back bundle exec rspec— 1623 examples, 0 failuresdocker compose exec back bundle exec rubocop— 593 files, no offenses関連