From 64f96fe01813d63783205e48e0f919624678f5c2 Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Thu, 25 Jun 2026 10:08:50 +0800 Subject: [PATCH] =?UTF-8?q?refactor(api):=20/v1/me=20=E6=94=B6=E6=95=9B=20?= =?UTF-8?q?expire=5Fat=20=E2=86=92=20=E5=8F=AA=E5=8F=91=20expires=5Fat(?= =?UTF-8?q?=E6=97=A0=20web=20=E7=94=A8=E6=88=B7=E4=B8=AD=E5=BF=83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 之前后端同时发 expire_at + expires_at(分别给 app + web 用户中心)。现已无 web 端, 双发是纯冗余,统一只用 expires_at: - account.go:meResponse 删 ExpireAt 字段 + GetMe handler 不再赋值 - client me.dart:fromJson 删 (expires_at ?? expire_at) 别名回退 - 两端契约快照同步:Go meResponse 冻结集去 expire_at(12→11 key)、删 Dart 别名测试 - 验证:go test httpapi 绿、flutter test contract(14)绿、analyze EXIT 0、e2e 全链路绿 Co-Authored-By: Claude Opus 4.8 --- client/lib/models/me.dart | 2 +- client/test/contract/api_contract_test.dart | 8 -------- server/internal/httpapi/account.go | 5 +---- server/internal/httpapi/contract_test.go | 7 +++---- 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/client/lib/models/me.dart b/client/lib/models/me.dart index 6165933..dc581da 100644 --- a/client/lib/models/me.dart +++ b/client/lib/models/me.dart @@ -43,7 +43,7 @@ class Me { bool get isFree => plan == 'free'; factory Me.fromJson(Map m) { - final exp = (m['expires_at'] ?? m['expire_at']) as String?; + final exp = m['expires_at'] as String?; return Me( email: m['email'] as String? ?? '', plan: m['plan'] as String? ?? 'free', diff --git a/client/test/contract/api_contract_test.dart b/client/test/contract/api_contract_test.dart index c1d8742..fb63bd3 100644 --- a/client/test/contract/api_contract_test.dart +++ b/client/test/contract/api_contract_test.dart @@ -87,14 +87,6 @@ void main() { expect(me.weeklyGb, hasLength(7)); expect(me.totpEnabled, isTrue); }); - - test('expires_at 接受别名 expire_at(后端历史兼容)', () { - final me = Me.fromJson({ - ..._meSample()..remove('expires_at'), - 'expire_at': '2027-01-01T00:00:00Z', - }); - expect(me.expiresAt, DateTime.tryParse('2027-01-01T00:00:00Z')); - }); }); group('/v1/usage 契约快照(UsagePoint)', () { diff --git a/server/internal/httpapi/account.go b/server/internal/httpapi/account.go index 4c53253..14b16d2 100644 --- a/server/internal/httpapi/account.go +++ b/server/internal/httpapi/account.go @@ -26,9 +26,7 @@ type meResponse struct { Email string `json:"email"` DpUUID string `json:"dp_uuid"` Plan string `json:"plan"` // "free" | "pro" | "team" - // expire_at kept for the existing app; expires_at is the same value for the - // web user-center (both RFC3339 UTC, null = no active sub). - ExpireAt *string `json:"expire_at"` + // expires_at: RFC3339 UTC, null = no active subscription. ExpiresAt *string `json:"expires_at"` // Web user-center fields. DevicesUsed int `json:"devices_used"` @@ -120,7 +118,6 @@ func (a *AccountAPI) GetMe(w http.ResponseWriter, r *http.Request) { } if expiresAt.Valid { s := expiresAt.Time.UTC().Format(time.RFC3339) - resp.ExpireAt = &s resp.ExpiresAt = &s } // quota_today_min: null when the plan is unlimited, else remaining minutes. diff --git a/server/internal/httpapi/contract_test.go b/server/internal/httpapi/contract_test.go index 8e64f3a..35775d1 100644 --- a/server/internal/httpapi/contract_test.go +++ b/server/internal/httpapi/contract_test.go @@ -6,8 +6,8 @@ package httpapi // 同步客户端 model + 文档,避免控制面与客户端悄悄分叉。 // // ⚠️ 与客户端冻结集对账:client/test/contract/api_contract_test.dart 的 _frozenMeKeys -// (9 个)。后端这里多发 uuid / dp_uuid,且同时发 expire_at + expires_at(别名):客户端 -// 故意忽略 uuid/dp_uuid、把 expire_at 当 expires_at 的回退。改任一端都要回看另一端。 +// (9 个)。后端这里多发 uuid / dp_uuid(客户端故意忽略);expire_at 已废弃移除—— +// 无 web 用户中心后,两端统一只用 expires_at。改任一端都要回看另一端。 import ( "reflect" @@ -54,8 +54,7 @@ func assertFrozenKeys(t *testing.T, name string, got map[string]struct{}, want . func TestContractMeResponse(t *testing.T) { assertFrozenKeys(t, "/v1/me", jsonTagSet(meResponse{}), - "uuid", "email", "dp_uuid", "plan", - "expire_at", "expires_at", + "uuid", "email", "dp_uuid", "plan", "expires_at", "devices_used", "devices_max", "quota_today_min", "data_today_gb", "weekly_gb", "totp_enabled", )