agent后台finance和users添加服务端分页和统计

This commit is contained in:
2026-05-10 08:41:49 +08:00
parent 3cbb607ff3
commit f3b9feca6a
5 changed files with 203 additions and 69 deletions
+116 -6
View File
@@ -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,
})
}
@@ -16,6 +16,7 @@ import DataTableToolbar from '@/pages/agent/finance/components/data-table-toolba
const props = defineProps<Omit<DataTableProps<Transaction>, 'columns'> & {
typeFilter?: string
serverPagination?: DataTableProps<Transaction>['serverPagination']
}>()
const emit = defineEmits<{
@@ -34,6 +35,7 @@ const table = generateVueTable<Transaction>({
get data() { return props.data },
get loading() { return props.loading },
columns: columns.value,
serverPagination: props.serverPagination,
})
const columnLabels: Record<string, string> = {
@@ -51,7 +53,7 @@ defineExpose({
</script>
<template>
<DataTable :columns="columns" :data :loading :table @refresh="emit('refresh')">
<DataTable :columns="columns" :data :loading :table :server-pagination @refresh="emit('refresh')">
<template #toolbar>
<div class="space-y-3">
<div class="flex flex-wrap items-center justify-between gap-2">
+45 -34
View File
@@ -16,42 +16,48 @@ const balance = ref(0)
const transactions = ref<Transaction[]>([])
const tableRef = ref()
const typeFilter = ref('')
const currentPage = ref(1)
const pageSize = ref(20)
const total = ref(0)
const rechargeCount = ref(0)
const consumeCount = ref(0)
const totalRecharge = ref(0)
const totalConsume = ref(0)
const filteredTransactions = computed(() => {
let result = transactions.value.filter(tx => tx)
if (typeFilter.value) {
result = result.filter(tx => tx.type === typeFilter.value)
}
return result
})
const serverPagination = computed(() => ({
page: currentPage.value,
pageSize: pageSize.value,
total: total.value,
onPageChange: (newPage: number) => {
currentPage.value = newPage
fetchFinance()
},
onPageSizeChange: (newPageSize: number) => {
pageSize.value = newPageSize
currentPage.value = 1
fetchFinance()
},
}))
const totalRecharge = computed(() => {
return transactions.value
.filter(tx => tx.type === 'recharge' || tx.type === 'refund')
.reduce((sum, tx) => sum + (tx.amount || 0), 0)
})
const totalConsume = computed(() => {
return transactions.value
.filter(tx => tx.type === 'consume')
.reduce((sum, tx) => sum + (tx.amount || 0), 0)
})
const rechargeCount = computed(() => transactions.value.filter(tx => tx.type === 'recharge' || tx.type === 'refund').length)
const consumeCount = computed(() => transactions.value.filter(tx => tx.type === 'consume').length)
onMounted(async () => {
async function fetchFinance() {
loading.value = true
try {
const data = await api.get<any>('/agent/finance')
const params = new URLSearchParams()
params.append('page', String(currentPage.value))
params.append('page_size', String(pageSize.value))
if (typeFilter.value) {
params.append('type', typeFilter.value)
}
const data = await api.get<any>(`/agent/finance?${params.toString()}`)
if (data) {
balance.value = data.balance || 0
if (Array.isArray(data.transactions)) {
transactions.value = data.transactions
} else if (Array.isArray(data.data)) {
transactions.value = data.data
} else {
transactions.value = []
}
transactions.value = data.transactions || []
total.value = data.total || 0
rechargeCount.value = data.recharge_count || 0
consumeCount.value = data.consume_count || 0
totalRecharge.value = data.total_recharge || 0
totalConsume.value = data.total_consume || 0
}
}
catch (error) {
@@ -61,6 +67,10 @@ onMounted(async () => {
finally {
loading.value = false
}
}
onMounted(() => {
fetchFinance()
})
</script>
@@ -166,10 +176,11 @@ onMounted(async () => {
v-else
ref="tableRef"
:loading
:data="filteredTransactions"
:data="transactions"
:server-pagination="serverPagination"
:type-filter="typeFilter"
@refresh="() => {}"
@update:typeFilter="typeFilter = $event"
@refresh="fetchFinance"
@update:typeFilter="typeFilter = $event; currentPage = 1; fetchFinance()"
/>
</UiCardContent>
</UiCard>
@@ -16,6 +16,7 @@ import DataTableToolbar from '@/pages/agent/users/components/data-table-toolbar.
const props = defineProps<Omit<DataTableProps<User>, 'columns'> & {
searchFilter?: string
serverPagination?: DataTableProps<User>['serverPagination']
}>()
const emit = defineEmits<{
@@ -34,6 +35,7 @@ const table = generateVueTable<User>({
get data() { return props.data },
get loading() { return props.loading },
columns: columns.value,
serverPagination: props.serverPagination,
})
const columnLabels: Record<string, string> = {
@@ -51,7 +53,7 @@ defineExpose({
</script>
<template>
<DataTable :columns="columns" :data :loading :table @refresh="emit('refresh')">
<DataTable :columns="columns" :data :loading :table :server-pagination @refresh="emit('refresh')">
<template #toolbar>
<div class="space-y-3">
<div class="flex flex-wrap items-center justify-between gap-2">
+36 -27
View File
@@ -15,36 +15,44 @@ const loading = ref(true)
const users = ref<User[]>([])
const tableRef = ref()
const searchFilter = ref('')
const currentPage = ref(1)
const pageSize = ref(20)
const total = ref(0)
const activeCount = ref(0)
const disabledCount = ref(0)
const bannedCount = ref(0)
const filteredUsers = computed(() => {
let result = users.value.filter(u => u)
if (searchFilter.value) {
const query = searchFilter.value.toLowerCase()
result = result.filter(u =>
u.username?.toLowerCase().includes(query)
|| u.email?.toLowerCase().includes(query),
)
}
return result
})
const activeCount = computed(() => users.value.filter(user => user.status === 'active').length)
const disabledCount = computed(() => users.value.filter(user => user.status === 'disabled').length)
const bannedCount = computed(() => users.value.filter(user => user.status === 'banned').length)
const serverPagination = computed(() => ({
page: currentPage.value,
pageSize: pageSize.value,
total: total.value,
onPageChange: (newPage: number) => {
currentPage.value = newPage
fetchUsers()
},
onPageSizeChange: (newPageSize: number) => {
pageSize.value = newPageSize
currentPage.value = 1
fetchUsers()
},
}))
async function fetchUsers() {
loading.value = true
try {
const data = await api.get<any>('/agent/users')
if (Array.isArray(data)) {
users.value = data
} else if (data?.users) {
users.value = data.users
} else if (data?.data) {
users.value = Array.isArray(data.data) ? data.data : []
} else {
users.value = []
const params = new URLSearchParams()
params.append('page', String(currentPage.value))
params.append('page_size', String(pageSize.value))
if (searchFilter.value) {
params.append('search', searchFilter.value.trim())
}
const data = await api.get<any>(`/agent/users?${params.toString()}`)
users.value = data?.users || []
total.value = data?.total || 0
activeCount.value = data?.active_count || 0
disabledCount.value = data?.disabled_count || 0
bannedCount.value = data?.banned_count || 0
}
catch (error) {
console.error('Load users failed:', error)
@@ -81,7 +89,7 @@ onMounted(() => {
</UiCardHeader>
<UiCardContent>
<div class="text-2xl font-bold">
{{ users.length }}
{{ total }}
</div>
</UiCardContent>
</UiCard>
@@ -139,10 +147,11 @@ onMounted(() => {
v-else
ref="tableRef"
:loading
:data="filteredUsers"
:data="users"
:server-pagination="serverPagination"
:search-filter="searchFilter"
@refresh="fetchUsers"
@update:search-filter="searchFilter = $event"
@update:search-filter="searchFilter = $event; currentPage = 1; fetchUsers()"
/>
</UiCardContent>
</UiCard>