Samurai-meet

機能仕様:募集カード・マッチング

最終更新: 2026-08-29

1. 対象

ユーザーが日時、時間帯、キーワード、公開半径を指定した募集カードを公開し、閲覧者の関心をカード所有者が承認したときだけマッチを成立させます。承認後はチャットと会合セッションを別APIで開放します。

現行コードでは募集・検索・関心・承認/辞退のGo APIと、外国人/日本人画面からの募集・募集管理・応募履歴フロー接続コードが存在します。日時入力の初期パース問題はISO内部値/Asia/Tokyo固定化と自動テストで解消済みです。2026-08-29時点で公開APIのhealthzとreadyzは確認済みで、iPhone実機では表示言語の設定変更が現在表示中の画面にも直ちに反映されることを確認しています。一方、日時pickerから公開・応募・通知遷移・取消・ログアウト後の戻る操作までを単一の手順として再現する全通しE2Eは未完了です。

対応要件:FR-007、FR-009、C-001、C-003、C-004、C-005

2. 募集カード項目

項目 必須 仕様
日時 Yes available_date。国内利用向けにAsia/Tokyo固定の壁時計として扱う
時間帯 Yes start_time / end_time
キーワード Yes 交流目的、言語、興味分野等。複数可
公開半径 Yes 1 km / 3 km / 5 km の選択式
位置 自動 有効な現在地から取得。正確な値は非公開
有効期限 自動 時間帯終了後に非表示
状態 自動 draft / open / matched / closed / expired
案内カテゴリ 自動 募集文をサーバー側のgemini-3.1-flash-liteでFood / Places / Activity / Otherの1つに分類する。Gemini APIキーはサーバー環境変数だけに置く

3. カード状態

card:  draft -> open -> matched
                  ├-> closed
                  └-> expired

match: pending -> accepted -> completed
       └-> cancelled(応募者が取り下げ)

4. マッチフロー

  1. ユーザー A がカードを閲覧する。
  2. A が「興味がある」を送る。
  3. カード所有者 B が承認する。
  4. matches.status = accepted となる。
  5. チャットを作成または開放する。
  6. 交流後、completed にして評価を有効にする。

5. 実装分担

処理 言語 / 場所
カード入力・プレビュー・状態表示 TypeScript / TSX
入力値の一次検証・フォーム状態 TypeScript
状態遷移、重複、期限、権限 Go
距離・公開条件 GoのHaversine + PostgreSQL(PostGIS未導入)
永続化 PostgreSQL / SQL

表示言語と利用モード

6. API / DB

募集カードの公開には名前・国コードが揃ったプロフィールが必要です。作成・更新の成功レスポンスは { "data": { ...card } }、検索は { "data": [ ...card ] } です。カードのレスポンスには正確な緯度・経度を含めず、現在地を使った場合だけ distance_band(within_1_km / within_3_km / within_5_km)を返します。

外国人側の募集確認に入る前に、POST /api/v1/recruitments/classifyが入力文をGeminiへ送り、Food、Places、Activity、Otherの案内カテゴリと最大5件の短いキーワード候補を返します。クライアントはAPIキーを保持せず、公開前にカテゴリ・候補キーワードを選び直せます。応答が契約外、未設定、またはGemini障害の場合はカテゴリを推測して公開せず、明示的なエラーにします。

検索条件は keyword(複数可)、available_date、start_time、end_time、radius_km(1/3/5)、verified_only、latitude、longitude、limit(最大50)です。緯度経度を省略した場合は、期限内の user_locations を使います。位置がない場合はキーワード・日時検索として扱い、位置による絞り込みは行いません。

現行のPostgreSQLイメージにはPostGISを追加していないため、距離計算はGoのHaversine計算です。座標はDB内だけで扱い、PostGISへの置換は性能改善タスクとして残します。

timezoneを省略または空にした募集入力はAsia/Tokyoへ正規化し、他のtimezoneは拒否します。created_atやexpires_atなどの絶対時刻はUTCで扱います。ISO内部値、JST固定の日時正規化、期限境界は自動テストで確認済みですが、過去時刻・日跨ぎ・端末日時pickerの実機確認は未完了です。

応募者はpending中に関心を取り下げられ、matchの状態はcancelledになります。取り下げ後の行は履歴と通知の冪等性のため保持します。

7. 受け入れ条件

8. 要確認

9. iPhone実機E2Eの確認手順

本項の全項目が同じ公開API環境で確認できるまで、この機能を実機E2E完了とは扱わない。

  1. 外国人モードで説明・日付・開始時刻・時間・半径を入力し、JSTの未来時刻で募集を公開する。空の説明、過去時刻、日跨ぎは公開されないことも確認する。
  2. 日本人モードへ切り替え、同じカードを検索して応募する。再度開いたカードでは重複送信ではなく既存応募の状態が表示されることを確認する。
  3. 募集者の通知から応募詳細を開き、承認または辞退する。表示言語を切り替えた後でも、同じ通知から正しい画面へ遷移できることを確認する。
  4. 応募者の応募履歴で結果を確認し、pendingなら取消できることを確認する。募集者は募集管理で応募者と状態を確認する。
  5. 画面復帰だけで一覧が再読込されないこと、明示的な更新操作では最新状態へ更新できることを確認する。
  6. ログアウト後に戻るジェスチャーを行っても保護画面へ戻らず、言語選択画面にとどまることを確認する。