From a0367b37e597394f8b6764e31f713a724fc4671a Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Sun, 12 Jul 2026 17:14:09 +0800 Subject: [PATCH] =?UTF-8?q?docs(plan):=20IA=20=E8=AE=A1=E5=88=92=E5=8A=A0?= =?UTF-8?q?=20Task=200=20=E8=AE=A2=E5=8D=95=E8=BF=87=E6=9C=9F=E6=80=81?= =?UTF-8?q?=E5=90=8E=E7=AB=AF=20+=20=E5=89=8D=E7=AB=AF=20expired=20?= =?UTF-8?q?=E5=A4=84=E7=90=86=20+=20=E9=83=A8=E7=BD=B2=E6=AD=A5=E9=AA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u --- .../plans/2026-07-12-account-ia-reorg.md | 122 +++++++++++++++++- 1 file changed, 117 insertions(+), 5 deletions(-) diff --git a/docs/superpowers/plans/2026-07-12-account-ia-reorg.md b/docs/superpowers/plans/2026-07-12-account-ia-reorg.md index 5317ee9..c1f9780 100644 --- a/docs/superpowers/plans/2026-07-12-account-ia-reorg.md +++ b/docs/superpowers/plans/2026-07-12-account-ia-reorg.md @@ -4,7 +4,7 @@ **Goal:** 把已评审的 IA(`docs/account-ia-reorg-design.html`)落成 Flutter:账户/设置/联系去重归位,Account 提为桌面一级项,顶栏加通知铃铛,账户页加邀请入口,并为 Spec ②推广激励 / ③系统通知 留占位屏与接口。 -**Architecture:** 复用现有 Riverpod + shell(desktop/mobile/tablet) + `open(NavView, mobilePage)` 下钻模式。多数是改造现有页面(account_page/settings_page/account_screens)+ 导航(navigation_provider/desktop_shell);新建仅 2 个占位屏(InviteScreen/NotificationsScreen)+ 铃铛 widget。邀请奖励引擎、通知下发机制**不在本计划**(各自 Spec)。 +**Architecture:** 复用现有 Riverpod + shell(desktop/mobile/tablet) + `open(NavView, mobilePage)` 下钻模式。多数是改造现有页面(account_page/settings_page/account_screens)+ 导航(navigation_provider/desktop_shell);新建仅 2 个占位屏(InviteScreen/NotificationsScreen)+ 铃铛 widget。**唯一后端改动 = 订单过期态(Task 0,display-only 零迁移)**——付款/订单核心后端已完成并部署生产。邀请奖励引擎、通知下发机制**不在本计划**(各自 Spec ②③,前后端一起)。 **Tech Stack:** Flutter / Dart · flutter_riverpod 2.x · 现有 `PangolinIcons`(生成,勿手改) · `AppText` 抽象基类 + 6 语言 strings。 @@ -18,6 +18,77 @@ --- +### Task 0: 后端 — 订单过期态(display-only,零迁移) + +**Files:** +- Modify: `server/internal/pay/handler.go`(`orderViewFromRow` + `orderPendingTTL` 常量) +- Test: `server/internal/pay/handler_test.go` + +**Interfaces:** +- Produces: 订单视图 `status` 新增可能值 `"expired"`(供前端映射)。判定:本地台账 `status=="created"` 且 `created_at + orderPendingTTL < now` → 视图显 `expired`(DB 不改,与金额兜底同为展示层)。 + +- [ ] **Step 1: 写过期判定测试(失败)** + +在 `handler_test.go` 加: +```go +// 老 created 单超 TTL → 列表视图显 expired;近期 created → 仍 created。 +func TestList_ExpiredWhenStale(t *testing.T) { + router, st := newHandlerRig(t, nil) + ctx := context.Background() + // 直接构造两条 created 单,改 created_at 到很久以前 / 刚刚 + if err := st.Insert(ctx, 1, "uuid-1", "pro_month", "old", "alipay", 2999, "CNY"); err != nil { t.Fatal(err) } + if err := st.Insert(ctx, 1, "uuid-1", "pro_month", "fresh", "alipay", 2999, "CNY"); err != nil { t.Fatal(err) } + // 把 old 的 created_at 回拨到 TTL 之前 + if _, err := st.db.ExecContext(ctx, + `UPDATE pay_purchases SET created_at = ? WHERE out_trade_no = 'old'`, + time.Now().Add(-2*orderPendingTTL).UTC()); err != nil { t.Fatal(err) } + w := httptest.NewRecorder() + router.ServeHTTP(w, authed(httptest.NewRequest(http.MethodGet, "/v1/pay/orders", nil), 1)) + var resp struct{ Orders []orderView `json:"orders"` } + _ = json.Unmarshal(w.Body.Bytes(), &resp) + got := map[string]string{} + for _, o := range resp.Orders { got[o.OrderNo] = o.Status } + if got["old"] != "expired" { t.Fatalf("old 应过期, got %q", got["old"]) } + if got["fresh"] != "created" { t.Fatalf("fresh 应 created, got %q", got["fresh"]) } +} +``` +> 注:`st.db` 是 `Store` 的私有字段,测试同包(`package pay`)可访问。 + +- [ ] **Step 2: 跑测试确认失败** + +Run: `cd server && go test ./internal/pay/ -run TestList_ExpiredWhenStale` +Expected: FAIL(old 仍为 created)。 + +- [ ] **Step 3: 实现过期判定** + +`handler.go` 加常量 + 在 `orderViewFromRow` 里判定: +```go +// 未付订单展示层过期窗口(近似 pay 会话时效;仅影响列表/详情显示,不改台账)。 +const orderPendingTTL = 30 * time.Minute +``` +在 `orderViewFromRow` 组装 `v` 后、`return v` 前加: +```go + if row.Status == "created" && row.CreatedAt.Add(orderPendingTTL).Before(time.Now()) { + v.Status = "expired" // 展示层:台账仍 created,可继续支付/取消 + } +``` + +- [ ] **Step 4: 跑测试确认通过** + +Run: `cd server && go test ./internal/pay/` +Expected: ok(含新测试 + 原有全绿)。 + +- [ ] **Step 5: Commit** + +```bash +git add server/internal/pay/handler.go server/internal/pay/handler_test.go +git commit -m "feat(server/pay): 订单展示层过期态(created 超 TTL 显 expired,零迁移)" +``` + +> **部署**:本任务改 server,需重编 linux 二进制 + 部署 pangolin1(Task 8 统一部署,或单独发)。 + +--- + ### Task 1: 导航枚举加 invite / notifications **Files:** @@ -87,6 +158,8 @@ git commit -m "feat(nav): NavView 加 invite/notifications + kAccountSubViews String get settingsGroupConn; // 连接 / Connection String get settingsGroupAppearance;// 外观 / Appearance String get settingsGroupAbout; // 关于 / About + // ── 订单过期态(Task 0 后端产出) ── + String get orderStatusExpired; // 已过期 / Expired ``` - [ ] **Step 2: 6 份 strings 各实现(带 @override)** @@ -365,6 +438,36 @@ git commit -m "feat(settings): 删购买/订单卡 + 归连接/外观/关于三 --- +### Task 7.5: 订单页处理 expired 态 + +**Files:** +- Modify: `client/lib/screens/orders_page.dart`(状态→pill 映射) + +**Interfaces:** +- Consumes: 后端 `status=="expired"`(Task 0)、`t.orderStatusExpired`(Task 2)。 + +- [ ] **Step 1: orders_page 状态映射加 expired** + +`orders_page.dart` 的 `_statusPill`(created→connecting / paid→connected / canceled→neutral)加一支: +```dart +'expired' => (status: PangolinStatus.neutral, label: t.orderStatusExpired), +``` +过期单动作沿用 created 的「继续支付」(台账仍可 retry;retry 若遇 pay 会话过期会 409→客户端已有重新下单兜底)。 + +- [ ] **Step 2: analyze** + +Run: `cd client && flutter analyze lib/screens/orders_page.dart` +Expected: No issues found。 + +- [ ] **Step 3: Commit** + +```bash +git add client/lib/screens/orders_page.dart +git commit -m "feat(client/orders): 订单列表/详情显示 expired 过期态" +``` + +--- + ### Task 8: 全量验收 + 收尾 **Files:** 无(验收) @@ -384,15 +487,24 @@ Expected: All tests passed。 Run: `cd /Users/wangjia/code/pangolin/.claude/worktrees/pay-v2-integration && node tools/check-l1-sync.mjs && node design/prototype/tools/check-ds.mjs` Expected: 图标同集 OK + l10n 完整 OK + check-ds 5 项过。 -- [ ] **Step 4: 更新决策日志** +- [ ] **Step 4: 服务端重编 + 部署(Task 0 改了 server)** -`docs/pay-v2-dev-log.md` 追加「IA 重构落地」step。 +```bash +cd server && go test ./... && GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -trimpath -o /tmp/pgl-new ./cmd/server +scp /tmp/pgl-new pangolin1:/tmp/pgl-new +ssh pangolin1 'cp -a /usr/local/bin/pangolin-server /usr/local/bin/pangolin-server.bak-ia && mv /tmp/pgl-new /usr/local/bin/pangolin-server && chmod +x /usr/local/bin/pangolin-server && systemctl restart pangolin-server && sleep 2 && systemctl is-active pangolin-server' +``` +Expected: `active`。验证 `curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8080/v1/pay/orders` → 401(路由在)。 -- [ ] **Step 5: Commit** +- [ ] **Step 5: 更新决策日志** + +`docs/pay-v2-dev-log.md` 追加「IA 重构落地 + 订单过期态」step。 + +- [ ] **Step 6: Commit** ```bash git add docs/pay-v2-dev-log.md -git commit -m "docs(pay-v2): IA 重构落地验收记录" +git commit -m "docs(pay-v2): IA 重构 + 订单过期态 落地验收记录" ``` ---