From 01b5a5ea08b6e68ac4f8bddee0093765f15ef923 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 1 Jul 2026 19:21:57 +0000 Subject: [PATCH] =?UTF-8?q?fix(status):=20=E5=86=8D=E8=A8=88=E7=AE=97?= =?UTF-8?q?=E7=B5=8C=E8=B7=AF=E3=81=A7=E3=82=82=20last=5Fsanpai=20?= =?UTF-8?q?=E3=82=92=E5=AE=9F=E9=9A=9B=E3=81=AE=E5=8F=82=E6=8B=9D=E6=97=A5?= =?UTF-8?q?=E6=99=82=E3=81=8B=E3=82=89=E8=BF=94=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit status_version 導入により、旧prodの既存ユーザー(status_version未設定)は 初回のstatus呼び出しで必ずフル再計算経路を通る。ところが再計算経路は last_sanpaiを実際のTimestampを読まず固定文字列『参拝していないようです』に していたため、参拝済みでも『参拝していないようです』と誤表示していた (2回目以降はキャッシュ経路で正しく表示=自己修復するが明確な回帰)。 本番statusGo?user=mattn を2回叩いて再現確認(1回目=誤表示/2回目=正しい日時)。 再計算経路でもキャッシュ経路と同じくトップレベルlast_sanpai(Timestamp)から 生成する(未参拝=ゼロ値のときのみ従来文言)。Go(status.go)/Node(index.js)両方を修正。 Co-authored-by: kojira --- app/functions-go/status.go | 7 ++++++- app/functions/index.js | 7 ++++++- docs/backend.md | 23 +++++++++++++++-------- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/app/functions-go/status.go b/app/functions-go/status.go index 057accc..604cc88 100644 --- a/app/functions-go/status.go +++ b/app/functions-go/status.go @@ -232,7 +232,12 @@ func statusHandler(w http.ResponseWriter, r *http.Request) { GithubImagePath: userData.ImagePath, }, }) - resp := StatusResponse{FormattedPerformance: formatted, LastSanpai: "参拝していないようです"} + // last_sanpai は cache-current パス(上)と同じく、キャッシュ有無に関わらず + // トップレベルの last_sanpai(Timestamp)から生成する。未参拝(ゼロ値)のときだけ + // formatLastSanpai が "参拝していないようです" を返す。 + // ※status_version 導入によりキャッシュ未計算の既存ユーザーは初回に必ずこの + // recompute パスを通るため、ここで固定文字列にすると参拝済みでも未参拝表示になる。 + resp := StatusResponse{FormattedPerformance: formatted, LastSanpai: formatLastSanpai(userData.LastSanpai)} if _, err := userDoc.Ref.Update(ctx, []firestore.Update{ {Path: "status", Value: toFirestoreStatus(formatted, resp.LastSanpai)}, diff --git a/app/functions/index.js b/app/functions/index.js index 9ed33da..128cade 100644 --- a/app/functions/index.js +++ b/app/functions/index.js @@ -348,7 +348,12 @@ exports.status = functions.https.onRequest(async (request, response) => { const raw_activities_list = await get_activity_list(userRef) const user_data = user_performance(raw_activities_list, request.query.user) return_Data = user_formatted_performance(user_data, appendData) - return_Data.last_sanpai = "参拝していないようです" + // last_sanpai はキャッシュ有無に関わらず last_sanpai(Timestamp)から生成する。 + // status_version 導入によりキャッシュ未計算の既存ユーザーは初回に必ずこのパスを + // 通るため、ここで固定文字列にすると参拝済みでも未参拝表示になってしまう。 + return_Data.last_sanpai = userData.last_sanpai + ? moment(userData.last_sanpai.toDate()).format('YYYY年MM月DD日 HH:mm') + : "参拝していないようです" await userRef.update({ status: return_Data, status_version: STATUS_LOGIC_VERSION diff --git a/docs/backend.md b/docs/backend.md index 901fc87..db3077e 100644 --- a/docs/backend.md +++ b/docs/backend.md @@ -117,14 +117,21 @@ Go(Cloud Run functions)で再実装し、`statusGo` という別関数名でデ - Node版との入出力の等価性は、Firestoreエミュレータに同一データを投入し、 両ハンドラの応答を比較して確認済み。 -### `last_sanpai` 未設定時のクラッシュバグの修正(Go/Node両方) - -`status` キャッシュ(`userData.status`)は存在するが `last_sanpai`(トップレベル)が -存在しないユーザー(一度も参拝せずプロフィールを2回以上表示した場合に発生し得る)に -対して、移植前のNode版は `undefined.toDate()` を呼び出して例外になるバグがあった。 -移植に合わせて、この場合は status 未保存時のフル計算パスと同じく -「参拝していないようです」を返すよう Go版・Node版の両方を修正した(未参拝ユーザーの -正しい表示。クラッシュしない)。 +### `status` レスポンスの `last_sanpai` の扱い(Go/Node両方) + +`status` レスポンスの `last_sanpai` は、**キャッシュ利用/フル再計算のどちらの経路でも** +ユーザードキュメントのトップレベル `last_sanpai`(Timestamp)から生成する +(`YYYY年MM月DD日 HH:mm`)。`last_sanpai` が未設定(ゼロ値/`undefined`)のときだけ +「参拝していないようです」を返す。 + +- 未設定ユーザー(一度も参拝せずプロフィールを表示した場合など)に対して、移植前のNode版は + `undefined.toDate()` を呼び出して例外になるバグがあった。これは Go版・Node版の両方で + ガードして「参拝していないようです」を返すよう修正済み(クラッシュしない)。 +- 以前はフル再計算経路のみ `last_sanpai` を固定文字列「参拝していないようです」にしていたため、 + **`status_version` 導入直後の既存(参拝済み)ユーザーは初回表示で必ずフル再計算経路を通り、 + 参拝済みでも「参拝していないようです」と誤表示される**回帰があった(2回目以降は + キャッシュ経路になり正しく表示)。フル再計算経路でもトップレベル `last_sanpai` から + 生成するよう両版を修正し、初回から正しい参拝日時を返す。 ## `sanpai` エンドポイントのGo移植 (`sanpaiGo`)