feat(server/pay): 订单展示层过期态(created 超 TTL 显 expired,零迁移)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-07-12 17:18:02 +08:00
parent a0367b37e5
commit b9e2f8c2da
2 changed files with 42 additions and 0 deletions
+6
View File
@@ -157,6 +157,9 @@ type orderView struct {
PaidAt *string `json:"paid_at,omitempty"`
}
// 未付订单展示层过期窗口(近似 pay 会话时效;仅影响列表/详情显示,不改台账)。
const orderPendingTTL = 30 * time.Minute
func orderViewFromRow(row *PurchaseRow) orderView {
v := orderView{
OrderNo: row.OutTradeNo, SKU: row.SKU, Method: row.Method, Status: row.Status,
@@ -178,6 +181,9 @@ func orderViewFromRow(row *PurchaseRow) orderView {
s := row.PaidAt.Time.UTC().Format(time.RFC3339)
v.PaidAt = &s
}
if row.Status == "created" && row.CreatedAt.Add(orderPendingTTL).Before(time.Now()) {
v.Status = "expired" // 展示层:台账仍 created,可继续支付/取消
}
return v
}
+36
View File
@@ -7,6 +7,7 @@ import (
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/go-chi/chi/v5"
@@ -289,3 +290,38 @@ func TestCatalog(t *testing.T) {
t.Fatalf("catalog = %+v", resp.Items)
}
}
// 老 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"])
}
}