合并 main 分支并融合订单列表最小化与 SQL 治理

- Auth/权限中间件取 main 演进版(错误细分、token 来源),保留 dev 的用户 cookie 与登出撤销语义
- 后台订单列表:融合 main 的首页免 COUNT 优化与关键词索引路由,保留 dev 的最小化 AdminOrderListItemDTO(结账明细仍由详情接口返回,同步调整对应集成测试)
- 保留 dev 的 token_version 冻结/解冻递增与 main 的状态变化判断及通知
- 前端:保留 dev 登出接口与 main 的 fetchMe silent 参数、SSE 登录跳转;后台订单表按 dev 最小化 DTO 移除号主到手列
This commit is contained in:
yml2213
2026-09-05 11:43:13 +08:00
294 changed files with 30171 additions and 13439 deletions
@@ -4,6 +4,8 @@ import (
"context"
"encoding/json"
"errors"
"strings"
"sync"
"testing"
"time"
@@ -694,6 +696,75 @@ func TestConfirmPaidRollsBackOrderWhenPaymentUpdateFails(t *testing.T) {
}
}
func TestConcurrentPaidNotificationsAreIdempotent(t *testing.T) {
db := database.NewTestDBWithName("payment-concurrent")
if err := database.MigrateRentalTransactionTestSchema(db); err != nil {
t.Fatalf("数据库迁移失败: %v", err)
}
sqlDB, err := db.DB()
if err != nil {
t.Fatalf("get sqlite db: %v", err)
}
sqlDB.SetMaxOpenConns(8)
orderRepo := ordermodule.NewRepository(db)
repo := NewRepository(db, nil, orderRepo)
fixture := createPayableOrderFixture(t, db, "CONCURRENT", true)
params := map[string]string{
"third_order_id": fixture.Payment.ThirdOrderID,
"status": "paid",
"amount": "1000",
"provider_order_id": "PROVIDER-CONCURRENT",
}
const callers = 8
results := make(chan error, callers)
var group sync.WaitGroup
for i := 0; i < callers; i++ {
group.Add(1)
go func() {
defer group.Done()
var callErr error
for attempt := 0; attempt < 50; attempt++ {
_, callErr = repo.HandleNotify(t.Context(), "mock", params, "status=paid", "application/x-www-form-urlencoded", "")
if callErr == nil || !strings.Contains(callErr.Error(), "locked") {
break
}
time.Sleep(5 * time.Millisecond)
}
results <- callErr
}()
}
group.Wait()
close(results)
for callErr := range results {
if callErr != nil {
t.Fatalf("concurrent HandleNotify() error = %v", callErr)
}
}
var latestPayment model.PaymentOrder
if err := db.First(&latestPayment, fixture.Payment.ID).Error; err != nil {
t.Fatalf("find payment: %v", err)
}
if latestPayment.Status != "paid" {
t.Fatalf("payment status = %q, want paid", latestPayment.Status)
}
var latestOrder model.RentalOrder
if err := db.First(&latestOrder, fixture.Order.ID).Error; err != nil {
t.Fatalf("find order: %v", err)
}
if latestOrder.Status != "pending_handoff" || latestOrder.HandoffStatus != "pending_owner" {
t.Fatalf("order status = %q/%q, want pending_handoff/pending_owner", latestOrder.Status, latestOrder.HandoffStatus)
}
var paidCount int64
if err := db.Model(&model.PaymentOrder{}).Where("id = ? AND status = 'paid'", fixture.Payment.ID).Count(&paidCount).Error; err != nil {
t.Fatalf("count paid payment: %v", err)
}
if paidCount != 1 {
t.Fatalf("paid payment rows = %d, want 1", paidCount)
}
}
// TestQueryClosedPaymentReturnsLocalStatus 验证关闭状态不再依赖渠道配置或继续轮询渠道。
func TestQueryClosedPaymentReturnsLocalStatus(t *testing.T) {
db := setupPaymentTestDB(t)