feat(backend): 授权订单列表接口——分页/筛选/汇总,支撑订单管理 tab
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -112,6 +113,39 @@ func (h *PayHandler) Cancel(c *gin.Context) {
|
||||
util.RespondSuccess(c, gin.H{"canceled": canceled})
|
||||
}
|
||||
|
||||
// Purchases GET /api/v1/license/purchases — 订单管理 tab 数据源:分页/status 筛选/汇总。
|
||||
// 仅管理员可查看(handler 内判权,同 Purchase)。
|
||||
func (h *PayHandler) Purchases(c *gin.Context) {
|
||||
role := middleware.GetRole(c)
|
||||
if role != "admin" && role != "superadmin" {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "仅管理员可查看订单"})
|
||||
return
|
||||
}
|
||||
page, _ := strconv.Atoi(c.Query("page"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
pageSize, _ := strconv.Atoi(c.Query("page_size"))
|
||||
if pageSize < 1 {
|
||||
pageSize = 20
|
||||
}
|
||||
if pageSize > 100 {
|
||||
pageSize = 100
|
||||
}
|
||||
status := c.Query("status")
|
||||
|
||||
list, err := h.svc.ListPurchases(middleware.GetShopID(c), page, pageSize, status)
|
||||
if err != nil {
|
||||
if errors.Is(err, service.ErrInvalidStatus) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
util.RespondSuccess(c, list)
|
||||
}
|
||||
|
||||
// PromoStatus GET /api/v1/license/promo-status — 本店首月特惠是否已享用(前端据此置灰特惠档)。
|
||||
func (h *PayHandler) PromoStatus(c *gin.Context) {
|
||||
used, err := h.svc.PromoUsed(middleware.GetShopID(c))
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/wangjia/jiu/backend/internal/middleware"
|
||||
"github.com/wangjia/jiu/backend/internal/model"
|
||||
"github.com/wangjia/jiu/backend/internal/service"
|
||||
"github.com/wangjia/jiu/backend/testutil"
|
||||
)
|
||||
|
||||
// setupPayRouter 独立路由:仅挂 payH.Purchases(仓里暂无 pay handler 测试先例,
|
||||
// 不复用 setupProtectedRouter 以免污染其他测试的路由表;判权写法与 Purchase/Cancel 同构)。
|
||||
func setupPayRouter(db *gorm.DB) *gin.Engine {
|
||||
payH := NewPayHandler(service.NewPayService(db, "http://pay.invalid", "test-secret", ""))
|
||||
|
||||
r := gin.New()
|
||||
r.Use(gin.Recovery())
|
||||
api := r.Group("/api/v1")
|
||||
api.Use(middleware.JWT(db))
|
||||
api.GET("/license/purchases", payH.Purchases)
|
||||
return r
|
||||
}
|
||||
|
||||
func seedPurchase(t *testing.T, db *gorm.DB, shopID, userID uint64, otn, status string, amountMinor int64) {
|
||||
t.Helper()
|
||||
require.NoError(t, db.Create(&model.LicensePurchase{
|
||||
ShopID: shopID, UserID: userID, ProductBizCode: "monthly_standard",
|
||||
AmountMinor: amountMinor, Currency: "CNY", OutTradeNo: otn, Status: status,
|
||||
}).Error)
|
||||
}
|
||||
|
||||
func TestPayHandler_Purchases_OperatorForbidden(t *testing.T) {
|
||||
db := testutil.SetupTestDB()
|
||||
shop := testutil.CreateTestShop(db, "PHDL01")
|
||||
op := testutil.CreateTestUser(db, shop.ID, "op1", "pass", "operator")
|
||||
token := getAuthToken(op.ID, shop.ID, "operator")
|
||||
r := setupPayRouter(db)
|
||||
|
||||
w := makeRequest(r, "GET", "/api/v1/license/purchases", token, nil)
|
||||
assert.Equal(t, http.StatusForbidden, w.Code)
|
||||
}
|
||||
|
||||
func TestPayHandler_Purchases_ReadonlyForbidden(t *testing.T) {
|
||||
db := testutil.SetupTestDB()
|
||||
shop := testutil.CreateTestShop(db, "PHDL02")
|
||||
ro := testutil.CreateTestUser(db, shop.ID, "ro1", "pass", "readonly")
|
||||
token := getAuthToken(ro.ID, shop.ID, "readonly")
|
||||
r := setupPayRouter(db)
|
||||
|
||||
w := makeRequest(r, "GET", "/api/v1/license/purchases", token, nil)
|
||||
assert.Equal(t, http.StatusForbidden, w.Code)
|
||||
}
|
||||
|
||||
func TestPayHandler_Purchases_AdminOK(t *testing.T) {
|
||||
db := testutil.SetupTestDB()
|
||||
shop := testutil.CreateTestShop(db, "PHDL03")
|
||||
admin := testutil.CreateTestUser(db, shop.ID, "admin1", "pass", "admin")
|
||||
token := getAuthToken(admin.ID, shop.ID, "admin")
|
||||
seedPurchase(t, db, shop.ID, admin.ID, "phdl-1", "paid", 29900)
|
||||
seedPurchase(t, db, shop.ID, admin.ID, "phdl-2", "pending", 29900)
|
||||
r := setupPayRouter(db)
|
||||
|
||||
w := makeRequest(r, "GET", "/api/v1/license/purchases", token, nil)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
resp := parseResponse(w)
|
||||
data, ok := resp["data"].(map[string]interface{})
|
||||
require.True(t, ok, "响应应含 data")
|
||||
assert.EqualValues(t, 2, data["total"])
|
||||
summary, ok := data["summary"].(map[string]interface{})
|
||||
require.True(t, ok)
|
||||
assert.EqualValues(t, 1, summary["paid_count"])
|
||||
assert.EqualValues(t, 1, summary["pending_count"])
|
||||
assert.EqualValues(t, 2, summary["total_count"])
|
||||
}
|
||||
|
||||
func TestPayHandler_Purchases_SuperadminOK(t *testing.T) {
|
||||
db := testutil.SetupTestDB()
|
||||
shop := testutil.CreateTestShop(db, "PHDL04")
|
||||
sa := testutil.CreateTestUser(db, shop.ID, "sa1", "pass", "superadmin")
|
||||
token := getAuthToken(sa.ID, shop.ID, "superadmin")
|
||||
r := setupPayRouter(db)
|
||||
|
||||
w := makeRequest(r, "GET", "/api/v1/license/purchases", token, nil)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
func TestPayHandler_Purchases_InvalidStatusBadRequest(t *testing.T) {
|
||||
db := testutil.SetupTestDB()
|
||||
shop := testutil.CreateTestShop(db, "PHDL05")
|
||||
admin := testutil.CreateTestUser(db, shop.ID, "admin2", "pass", "admin")
|
||||
token := getAuthToken(admin.ID, shop.ID, "admin")
|
||||
r := setupPayRouter(db)
|
||||
|
||||
w := makeRequest(r, "GET", "/api/v1/license/purchases?status=bogus", token, nil)
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func TestPayHandler_Purchases_PageSizeClamped(t *testing.T) {
|
||||
db := testutil.SetupTestDB()
|
||||
shop := testutil.CreateTestShop(db, "PHDL06")
|
||||
admin := testutil.CreateTestUser(db, shop.ID, "admin3", "pass", "admin")
|
||||
token := getAuthToken(admin.ID, shop.ID, "admin")
|
||||
for i := 0; i < 3; i++ {
|
||||
seedPurchase(t, db, shop.ID, admin.ID, fmt.Sprintf("phdl6-%d", i), "paid", 100)
|
||||
}
|
||||
r := setupPayRouter(db)
|
||||
|
||||
// page_size 请求 500,服务端应上限 clamp 到 100(不报错,正常 200)
|
||||
w := makeRequest(r, "GET", "/api/v1/license/purchases?page_size=500", token, nil)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
Reference in New Issue
Block a user