feat(admin-payment): 支付订单 - 补充用户身份与详情
This commit is contained in:
1 parent
15e5b4a8bf
commit
abc7481ac1
9 files changed
+201
-39
No files matched your search
@@ -101,10 +101,22 @@ type PaymentOrderView struct {
|
||||
}
|
||||
|
||||
type AdminPaymentOrderPage struct {
|
||||
Orders []PaymentOrderView `json:"orders"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
Limit int `json:"pageSize"`
|
||||
Orders []AdminPaymentOrderView `json:"orders"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
Limit int `json:"pageSize"`
|
||||
}
|
||||
|
||||
type AdminPaymentOrderUser struct {
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
type AdminPaymentOrderView struct {
|
||||
PaymentOrderView
|
||||
User *AdminPaymentOrderUser `json:"user"`
|
||||
}
|
||||
|
||||
func (s *Service) PaymentNotificationResponse(providerID string, success bool) (int, string, string) {
|
||||
@@ -876,9 +888,21 @@ func (s *Service) AdminPaymentOrderPage(actor *model.User, status, keyword strin
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
views := make([]PaymentOrderView, 0, len(orders))
|
||||
userIDs := make([]string, 0, len(orders))
|
||||
for _, order := range orders {
|
||||
views = append(views, paymentOrderView(order))
|
||||
userIDs = append(userIDs, order.UserID)
|
||||
}
|
||||
users, err := s.repo.UsersByIDs(userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
views := make([]AdminPaymentOrderView, 0, len(orders))
|
||||
for _, order := range orders {
|
||||
view := AdminPaymentOrderView{PaymentOrderView: paymentOrderView(order)}
|
||||
if user, ok := users[order.UserID]; ok {
|
||||
view.User = &AdminPaymentOrderUser{ID: user.ID, Username: user.Username, DisplayName: user.DisplayName, Email: user.Email}
|
||||
}
|
||||
views = append(views, view)
|
||||
}
|
||||
return &AdminPaymentOrderPage{Orders: views, Total: total, Page: page, Limit: limit}, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPaymentOrderIdentityOnlyInAdminList(t *testing.T) {
|
||||
base := PaymentOrderView{ID: "order", UserID: "user", MerchantOrderNo: "merchant"}
|
||||
ordinary, err := json.Marshal(base)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Contains(string(ordinary), `"user":`) || strings.Contains(string(ordinary), `"email":`) {
|
||||
t.Fatalf("ordinary response includes identity: %s", ordinary)
|
||||
}
|
||||
admin, err := json.Marshal(AdminPaymentOrderView{PaymentOrderView: base, User: &AdminPaymentOrderUser{ID: "user", Username: "alice", DisplayName: "小林", Email: "alice@example.com"}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var result struct {
|
||||
ID string `json:"id"`
|
||||
User *AdminPaymentOrderUser `json:"user"`
|
||||
}
|
||||
if err := json.Unmarshal(admin, &result); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if result.ID != "order" || result.User == nil || result.User.Email != "alice@example.com" {
|
||||
t.Fatalf("invalid admin response: %s", admin)
|
||||
}
|
||||
missing, err := json.Marshal(AdminPaymentOrderView{PaymentOrderView: base})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(string(missing), `"user":null`) || !strings.Contains(string(missing), `"userId":"user"`) {
|
||||
t.Fatalf("missing user loses original identity: %s", missing)
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ func (r *Repository) UsersByIDs(ids []string) (map[string]model.User, error) {
|
||||
return map[string]model.User{}, nil
|
||||
}
|
||||
var users []model.User
|
||||
if err := r.db.Select("id", "username", "display_name").Where("id IN ?", ids).Find(&users).Error; err != nil {
|
||||
if err := r.db.Select("id", "username", "display_name", "email").Where("id IN ?", ids).Find(&users).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make(map[string]model.User, len(users))
|
||||
|
||||
@@ -391,7 +391,8 @@ func (r *Repository) AdminPaymentOrders(status, keyword string, limit, offset in
|
||||
}
|
||||
if normalized := strings.TrimSpace(keyword); normalized != "" {
|
||||
like := "%" + normalized + "%"
|
||||
query = query.Where("merchant_order_no LIKE ? OR provider_trade_no LIKE ? OR user_id LIKE ?", like, like, like)
|
||||
users := r.db.Model(&model.User{}).Select("id").Where("LOWER(username) LIKE ? OR LOWER(display_name) LIKE ? OR LOWER(email) LIKE ?", strings.ToLower(like), strings.ToLower(like), strings.ToLower(like))
|
||||
query = query.Where("merchant_order_no LIKE ? OR provider_trade_no LIKE ? OR user_id = ? OR user_id IN (?)", like, like, normalized, users)
|
||||
}
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
|
||||
@@ -363,6 +363,50 @@ func TestUnresolvedPaymentOrderCandidateCountOverlapping(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminPaymentOrdersSearchUserIdentity(t *testing.T) {
|
||||
db := openPaymentTestDB(t)
|
||||
if err := db.AutoMigrate(&model.User{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
users := []model.User{
|
||||
{ID: "user-alice", Username: "alice", DisplayName: "小林", Email: "alice@example.com"},
|
||||
{ID: "user-bob", Username: "bob", DisplayName: "小林", Email: "bob@example.com"},
|
||||
}
|
||||
if err := db.Create(&users).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
orders := []model.PaymentOrder{
|
||||
{ID: "a", UserID: "user-alice", MerchantOrderNo: "merchant-a", IdempotencyKey: "a", Status: model.PaymentOrderPending},
|
||||
{ID: "b", UserID: "user-bob", MerchantOrderNo: "merchant-b", IdempotencyKey: "b", Status: model.PaymentOrderClosed},
|
||||
{ID: "c", UserID: "missing-user", MerchantOrderNo: "merchant-c", IdempotencyKey: "c", Status: model.PaymentOrderClosed},
|
||||
}
|
||||
tradeNo := "trade-c"
|
||||
orders[2].ProviderTradeNo = &tradeNo
|
||||
if err := db.Create(&orders).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
repo := New(db)
|
||||
for _, tc := range []struct {
|
||||
keyword, status string
|
||||
total int64
|
||||
}{
|
||||
{"ALICE@EXAMPLE.COM", "", 1}, {"alice", "", 1}, {"小林", "", 2},
|
||||
{"小林", "pending", 1}, {"alice", "closed", 0}, {"user-bob", "", 1},
|
||||
{"merchant-c", "", 1}, {"trade-c", "", 1}, {"missing-user", "", 1}, {"", "", 3},
|
||||
} {
|
||||
t.Run(tc.keyword+"/"+tc.status, func(t *testing.T) {
|
||||
items, total, err := repo.AdminPaymentOrders(tc.status, tc.keyword, 1, 0)
|
||||
if err != nil || total != tc.total || len(items) > 1 {
|
||||
t.Fatalf("got items=%d total=%d err=%v; want total=%d", len(items), total, err, tc.total)
|
||||
}
|
||||
})
|
||||
}
|
||||
identities, err := repo.UsersByIDs([]string{"user-alice", "missing-user"})
|
||||
if err != nil || len(identities) != 1 || identities["user-alice"].Email != "alice@example.com" {
|
||||
t.Fatalf("identities=%v err=%v", identities, err)
|
||||
}
|
||||
}
|
||||
|
||||
func openPaymentTestDB(t *testing.T) *gorm.DB {
|
||||
t.Helper()
|
||||
db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared&_busy_timeout=5000"), &gorm.Config{})
|
||||
|
||||
Reference in new issue
Block a user