diff --git a/backend/internal/router/agent/agent.go b/backend/internal/router/agent/agent.go index f762b0c..1cf3b14 100644 --- a/backend/internal/router/agent/agent.go +++ b/backend/internal/router/agent/agent.go @@ -3,6 +3,7 @@ package agent import ( "fmt" "math/rand" + "sort" "strconv" "strings" "time" @@ -432,6 +433,11 @@ func handleGetUsers(c *gin.Context) { var total int64 database.DB.Model(&model.AppUser{}).Where("application_id IN ?", appIDs).Count(&total) + var activeCount, disabledCount, bannedCount int64 + database.DB.Model(&model.AppUser{}).Where("application_id IN ? AND status = ?", appIDs, "active").Count(&activeCount) + database.DB.Model(&model.AppUser{}).Where("application_id IN ? AND status = ?", appIDs, "disabled").Count(&disabledCount) + database.DB.Model(&model.AppUser{}).Where("application_id IN ? AND status = ?", appIDs, "banned").Count(&bannedCount) + var users []model.AppUser offset := 0 if pageInt, err := strconv.Atoi(page); err == nil && pageInt > 1 { @@ -446,8 +452,11 @@ func handleGetUsers(c *gin.Context) { database.DB.Where("application_id IN ?", appIDs).Order("created_at DESC").Limit(limit).Offset(offset).Find(&users) response.Success(c, gin.H{ - "users": users, - "total": total, + "users": users, + "total": total, + "active_count": activeCount, + "disabled_count": disabledCount, + "banned_count": bannedCount, }) } @@ -460,12 +469,113 @@ func handleGetFinance(c *gin.Context) { return } - var records []model.RechargeRecord - database.DB.Where("user_id = ?", userID).Order("created_at DESC").Limit(20).Find(&records) + typeFilter := c.Query("type") + page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) + pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) + + type TransactionItem struct { + ID uint `json:"id"` + Type string `json:"type"` + Amount float64 `json:"amount"` + Description string `json:"description"` + Balance float64 `json:"balance"` + CreatedAt string `json:"created_at"` + } + + var allTransactions []TransactionItem + + var rechargeRecords []model.RechargeRecord + database.DB.Where("user_id = ? AND status = ?", userID, "success").Order("created_at DESC").Find(&rechargeRecords) + for _, r := range rechargeRecords { + txType := "recharge" + if r.PaymentType == "refund" || r.Remark != "" && strings.Contains(strings.ToLower(r.Remark), "refund") { + txType = "refund" + } + desc := r.Remark + if desc == "" { + desc = "充值" + } + allTransactions = append(allTransactions, TransactionItem{ + ID: r.ID, + Type: txType, + Amount: r.Amount, + Description: desc, + Balance: user.Balance, + CreatedAt: r.CreatedAt.Format("2006-01-02T15:04:05Z07:00"), + }) + } + + var consumptionRecords []model.ConsumptionRecord + database.DB.Where("user_id = ?", userID).Order("created_at DESC").Find(&consumptionRecords) + for _, r := range consumptionRecords { + desc := r.Description + if desc == "" { + desc = r.Content + } + if desc == "" { + desc = "消费" + } + allTransactions = append(allTransactions, TransactionItem{ + ID: r.ID, + Type: "consume", + Amount: r.Amount, + Description: desc, + Balance: r.BalanceAfter, + CreatedAt: r.CreatedAt.Format("2006-01-02T15:04:05Z07:00"), + }) + } + + sort.Slice(allTransactions, func(i, j int) bool { + return allTransactions[i].CreatedAt > allTransactions[j].CreatedAt + }) + + var filtered []TransactionItem + if typeFilter != "" { + for _, tx := range allTransactions { + if tx.Type == typeFilter { + filtered = append(filtered, tx) + } + } + } else { + filtered = allTransactions + } + + total := len(filtered) + + rechargeCount := 0 + consumeCount := 0 + var totalRecharge, totalConsume float64 + for _, tx := range allTransactions { + if tx.Type == "recharge" || tx.Type == "refund" { + rechargeCount++ + totalRecharge += tx.Amount + } else if tx.Type == "consume" { + consumeCount++ + totalConsume += tx.Amount + } + } + + offset := (page - 1) * pageSize + end := offset + pageSize + if offset > total { + offset = total + } + if end > total { + end = total + } + + paginated := filtered[offset:end] response.Success(c, gin.H{ - "balance": user.Balance, - "records": records, + "balance": user.Balance, + "transactions": paginated, + "total": total, + "page": page, + "page_size": pageSize, + "recharge_count": rechargeCount, + "consume_count": consumeCount, + "total_recharge": totalRecharge, + "total_consume": totalConsume, }) } diff --git a/frontend/src/pages/agent/finance/components/data-table.vue b/frontend/src/pages/agent/finance/components/data-table.vue index 1f8dcc7..6a6621f 100644 --- a/frontend/src/pages/agent/finance/components/data-table.vue +++ b/frontend/src/pages/agent/finance/components/data-table.vue @@ -16,6 +16,7 @@ import DataTableToolbar from '@/pages/agent/finance/components/data-table-toolba const props = defineProps, 'columns'> & { typeFilter?: string + serverPagination?: DataTableProps['serverPagination'] }>() const emit = defineEmits<{ @@ -34,6 +35,7 @@ const table = generateVueTable({ get data() { return props.data }, get loading() { return props.loading }, columns: columns.value, + serverPagination: props.serverPagination, }) const columnLabels: Record = { @@ -51,7 +53,7 @@ defineExpose({