松戸八柱店から「テンマネの会員タブで会員名検索してもヒットしない」と問合せ。報告者(Masaru Suzuki)自身も確認し、2名の会員が会員名検索でヒットしないことを確認。購入履歴タブからは検索可能。
/api/users/search の検索ロジックを修正。RECURRING 指定時に、最新1件の type 判定ではなく EXISTS サブクエリで「履歴に RECURRING があるか」で判定するように変更。
フロントエンドが送信する固定フィルタと、バックエンドの「最新1件のみ判定」ロジックの組み合わせにより、過去に定期会員だったが現在は単発のユーザーが除外される。
membershipType: 'RECURRING' を常に固定送信ROW_NUMBER() PARTITION BY user_id ORDER BY id DESCtype = 'RECURRING' で判定
ONE_SHOT ならRECURRING 固定は偶然ではなく、意図的な設計であることがコードとコミット履歴から確認できた。
会員タブは「定期課金ユーザー」の管理を目的として設計されている。チケット(ONE_SHOT)はサブスク編集フォームでも「チケット」として別セクションに分離表示。
もともとフロントエンド側で userMemberships.find(m => m.type == 'RECURRING') でフィルタしていた。2025-02-27 のコミットでAPIレベルに移行しただけで、フィルタ自体は初期設計から存在。
会員一覧画面のフィルタに定期/単発の切り替えUIは存在しない。検索キーワード・スタジオ・ステータス・トライアルのみ。
CSV出力時も membershipType: 'RECURRING' を固定送信。さらにCSV生成ユーティリティ側でも RECURRING でフィルタしている二重構造。
RECURRING 指定時だけ「履歴にあるか」で判定し、ONE_SHOT 指定時は「最新1件のtype」で判定する。同じパラメータの挙動が値によって異なるのは、今後の混乱の原因になる。status IN (:statuses) でフィルタしているが、追加された EXISTS サブクエリ(um_hist)には status 条件がない。EXPIRED 等の無効なメンバーシップでもヒットしてしまう。studioIds || fallback でフォールバック値を設定するが、EXISTS 側では studioIds && studioIds.length > 0 でガード。2箇所の条件が微妙に異なる。user_memberships を (user_id, type) で走査する。該当インデックスの有無が未確認。根本原因は「フロントエンドが RECURRING を固定送信している」こと。バックエンドで RECURRING の意味を暗黙的に変えるのではなく、フロントエンド側でフィルタを制御する方が設計として自然。
会員一覧画面にフィルタ切り替えUIを追加し、ユーザーが明示的に絞り込みを選べるようにする。
membership_type は null 対応済み)PR #858 のアプローチ。RECURRING 指定時に EXISTS で履歴判定。