修复: 1.管理员编辑代理云端数据权限switch无效(后端缺少CanViewCloudData字段) 2.代理卡密status显示disabledbanned 3.代理用户管理只显示代理/子代理卡密充值过的用户
This commit is contained in:
@@ -29,6 +29,7 @@ type User struct {
|
|||||||
|
|
||||||
ParentAgentID *uint `gorm:"index" json:"parent_agent_id"`
|
ParentAgentID *uint `gorm:"index" json:"parent_agent_id"`
|
||||||
CanCreateAgent bool `gorm:"default:false" json:"can_create_agent"`
|
CanCreateAgent bool `gorm:"default:false" json:"can_create_agent"`
|
||||||
|
CanViewCloudData bool `gorm:"default:false" json:"can_view_cloud_data"`
|
||||||
Balance float64 `gorm:"default:0" json:"balance"`
|
Balance float64 `gorm:"default:0" json:"balance"`
|
||||||
TotalConsumption float64 `gorm:"default:0" json:"total_consumption"`
|
TotalConsumption float64 `gorm:"default:0" json:"total_consumption"`
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ type AgentTreeNode struct {
|
|||||||
CreatedAt string `json:"created_at"`
|
CreatedAt string `json:"created_at"`
|
||||||
LastLoginAt string `json:"last_login_at"`
|
LastLoginAt string `json:"last_login_at"`
|
||||||
Balance float64 `json:"balance"`
|
Balance float64 `json:"balance"`
|
||||||
CanCreateAgent bool `json:"can_create_agent"`
|
CanCreateAgent bool `json:"can_create_agent"`
|
||||||
|
CanViewCloudData bool `json:"can_view_cloud_data"`
|
||||||
CardsCount int `json:"cards_count"`
|
CardsCount int `json:"cards_count"`
|
||||||
ChildAgentsCount int `json:"child_agents_count"`
|
ChildAgentsCount int `json:"child_agents_count"`
|
||||||
Children []AgentTreeNode `json:"children,omitempty"`
|
Children []AgentTreeNode `json:"children,omitempty"`
|
||||||
@@ -80,6 +81,7 @@ func handleGetAgents(c *gin.Context) {
|
|||||||
Balance float64 `json:"balance"`
|
Balance float64 `json:"balance"`
|
||||||
TotalConsumption float64 `json:"total_consumption"`
|
TotalConsumption float64 `json:"total_consumption"`
|
||||||
CanCreateAgent bool `json:"can_create_agent"`
|
CanCreateAgent bool `json:"can_create_agent"`
|
||||||
|
CanViewCloudData bool `json:"can_view_cloud_data"`
|
||||||
CardsCount int `json:"cards_count"`
|
CardsCount int `json:"cards_count"`
|
||||||
ChildAgentsCount int `json:"child_agents_count"`
|
ChildAgentsCount int `json:"child_agents_count"`
|
||||||
}
|
}
|
||||||
@@ -152,8 +154,9 @@ func handleGetAgents(c *gin.Context) {
|
|||||||
LastLoginAt: lastLoginAt,
|
LastLoginAt: lastLoginAt,
|
||||||
Balance: user.Balance,
|
Balance: user.Balance,
|
||||||
TotalConsumption: user.TotalConsumption,
|
TotalConsumption: user.TotalConsumption,
|
||||||
CanCreateAgent: user.CanCreateAgent,
|
CanCreateAgent: user.CanCreateAgent,
|
||||||
CardsCount: cardsCountMap[user.ID],
|
CanViewCloudData: user.CanViewCloudData,
|
||||||
|
CardsCount: cardsCountMap[user.ID],
|
||||||
ChildAgentsCount: childCountMap[user.ID],
|
ChildAgentsCount: childCountMap[user.ID],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -229,8 +232,9 @@ func handleGetAgentsTree(c *gin.Context) {
|
|||||||
CreatedAt: user.CreatedAt.Format("2006-01-02 15:04:05"),
|
CreatedAt: user.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||||
LastLoginAt: lastLoginAt,
|
LastLoginAt: lastLoginAt,
|
||||||
Balance: user.Balance,
|
Balance: user.Balance,
|
||||||
CanCreateAgent: user.CanCreateAgent,
|
CanCreateAgent: user.CanCreateAgent,
|
||||||
CardsCount: int(cardsCount),
|
CanViewCloudData: user.CanViewCloudData,
|
||||||
|
CardsCount: int(cardsCount),
|
||||||
ChildAgentsCount: int(childAgentsCount),
|
ChildAgentsCount: int(childAgentsCount),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,12 +260,13 @@ func handleGetAgentsTree(c *gin.Context) {
|
|||||||
|
|
||||||
func handleCreateAgent(c *gin.Context) {
|
func handleCreateAgent(c *gin.Context) {
|
||||||
var req struct {
|
var req struct {
|
||||||
Username string `json:"username" binding:"required"`
|
Username string `json:"username" binding:"required"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Password string `json:"password" binding:"required"`
|
Password string `json:"password" binding:"required"`
|
||||||
ParentAgentID *uint `json:"parent_agent_id"`
|
ParentAgentID *uint `json:"parent_agent_id"`
|
||||||
CanCreateAgent bool `json:"can_create_agent"`
|
CanCreateAgent bool `json:"can_create_agent"`
|
||||||
Balance float64 `json:"balance"`
|
CanViewCloudData bool `json:"can_view_cloud_data"`
|
||||||
|
Balance float64 `json:"balance"`
|
||||||
}
|
}
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
response.Error(c, 400, "参数错误")
|
response.Error(c, 400, "参数错误")
|
||||||
@@ -296,13 +301,14 @@ func handleCreateAgent(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
user := model.User{
|
user := model.User{
|
||||||
Username: req.Username,
|
Username: req.Username,
|
||||||
Password: string(hashedPassword),
|
Password: string(hashedPassword),
|
||||||
Role: "agent",
|
Role: "agent",
|
||||||
Status: "active",
|
Status: "active",
|
||||||
ParentAgentID: req.ParentAgentID,
|
ParentAgentID: req.ParentAgentID,
|
||||||
CanCreateAgent: req.CanCreateAgent,
|
CanCreateAgent: req.CanCreateAgent,
|
||||||
Balance: req.Balance,
|
CanViewCloudData: req.CanViewCloudData,
|
||||||
|
Balance: req.Balance,
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Email != "" {
|
if req.Email != "" {
|
||||||
@@ -406,7 +412,8 @@ func handleGetAgentDetail(c *gin.Context) {
|
|||||||
"role": user.Role,
|
"role": user.Role,
|
||||||
"parent_agent_id": user.ParentAgentID,
|
"parent_agent_id": user.ParentAgentID,
|
||||||
"parent_agent_name": parentAgentName,
|
"parent_agent_name": parentAgentName,
|
||||||
"can_create_agent": user.CanCreateAgent,
|
"can_create_agent": user.CanCreateAgent,
|
||||||
|
"can_view_cloud_data": user.CanViewCloudData,
|
||||||
"balance": user.Balance,
|
"balance": user.Balance,
|
||||||
"created_at": user.CreatedAt.Format("2006-01-02 15:04:05"),
|
"created_at": user.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||||
"last_login_at": lastLoginAt,
|
"last_login_at": lastLoginAt,
|
||||||
@@ -426,11 +433,12 @@ func handleUpdateAgent(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var req struct {
|
var req struct {
|
||||||
ParentAgentID *uint `json:"parent_agent_id"`
|
ParentAgentID *uint `json:"parent_agent_id"`
|
||||||
Email *string `json:"email"`
|
Email *string `json:"email"`
|
||||||
Password *string `json:"password"`
|
Password *string `json:"password"`
|
||||||
CanCreateAgent *bool `json:"can_create_agent"`
|
CanCreateAgent *bool `json:"can_create_agent"`
|
||||||
Balance *float64 `json:"balance"`
|
CanViewCloudData *bool `json:"can_view_cloud_data"`
|
||||||
|
Balance *float64 `json:"balance"`
|
||||||
}
|
}
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
response.Error(c, 400, "参数错误")
|
response.Error(c, 400, "参数错误")
|
||||||
@@ -464,6 +472,9 @@ func handleUpdateAgent(c *gin.Context) {
|
|||||||
if req.CanCreateAgent != nil {
|
if req.CanCreateAgent != nil {
|
||||||
user.CanCreateAgent = *req.CanCreateAgent
|
user.CanCreateAgent = *req.CanCreateAgent
|
||||||
}
|
}
|
||||||
|
if req.CanViewCloudData != nil {
|
||||||
|
user.CanViewCloudData = *req.CanViewCloudData
|
||||||
|
}
|
||||||
if req.Balance != nil {
|
if req.Balance != nil {
|
||||||
user.Balance = *req.Balance
|
user.Balance = *req.Balance
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,8 +181,8 @@ func handleGetCards(c *gin.Context) {
|
|||||||
var expiredCount int64
|
var expiredCount int64
|
||||||
database.DB.Model(&model.Card{}).Where("("+baseCondition+") AND status = ?", userID, userID, "expired").Count(&expiredCount)
|
database.DB.Model(&model.Card{}).Where("("+baseCondition+") AND status = ?", userID, userID, "expired").Count(&expiredCount)
|
||||||
|
|
||||||
var disabledCount int64
|
var bannedCount int64
|
||||||
database.DB.Model(&model.Card{}).Where("("+baseCondition+") AND status = ?", userID, userID, "disabled").Count(&disabledCount)
|
database.DB.Model(&model.Card{}).Where("("+baseCondition+") AND status = ?", userID, userID, "banned").Count(&bannedCount)
|
||||||
|
|
||||||
query := database.DB.Model(&model.Card{}).Where(baseCondition, userID, userID)
|
query := database.DB.Model(&model.Card{}).Where(baseCondition, userID, userID)
|
||||||
|
|
||||||
@@ -283,7 +283,7 @@ func handleGetCards(c *gin.Context) {
|
|||||||
"unused_count": unusedCount,
|
"unused_count": unusedCount,
|
||||||
"used_count": usedCount,
|
"used_count": usedCount,
|
||||||
"expired_count": expiredCount,
|
"expired_count": expiredCount,
|
||||||
"disabled_count": disabledCount,
|
"banned_count": bannedCount,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,17 +412,40 @@ func generateCardCode() string {
|
|||||||
func handleGetUsers(c *gin.Context) {
|
func handleGetUsers(c *gin.Context) {
|
||||||
userID := c.GetUint("user_id")
|
userID := c.GetUint("user_id")
|
||||||
|
|
||||||
var agentApps []model.AgentApplication
|
agentIDs := []uint{userID}
|
||||||
database.DB.Where("agent_id = ?", userID).Find(&agentApps)
|
var childAgents []model.User
|
||||||
appIDs := make([]uint, 0)
|
database.DB.Where("parent_agent_id = ? AND role = ?", userID, "agent").Find(&childAgents)
|
||||||
for _, app := range agentApps {
|
for _, child := range childAgents {
|
||||||
appIDs = append(appIDs, app.ApplicationID)
|
agentIDs = append(agentIDs, child.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(appIDs) == 0 {
|
var cardIDs []uint
|
||||||
|
database.DB.Model(&model.Card{}).Where("agent_id IN ?", agentIDs).Pluck("id", &cardIDs)
|
||||||
|
|
||||||
|
if len(cardIDs) == 0 {
|
||||||
response.Success(c, gin.H{
|
response.Success(c, gin.H{
|
||||||
"users": []interface{}{},
|
"users": []interface{}{},
|
||||||
"total": 0,
|
"total": 0,
|
||||||
|
"active_count": 0,
|
||||||
|
"disabled_count": 0,
|
||||||
|
"banned_count": 0,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var appUserIDs []uint
|
||||||
|
database.DB.Model(&model.RechargeRecord{}).
|
||||||
|
Where("card_id IN ? AND status = ?", cardIDs, "success").
|
||||||
|
Distinct("user_id").
|
||||||
|
Pluck("user_id", &appUserIDs)
|
||||||
|
|
||||||
|
if len(appUserIDs) == 0 {
|
||||||
|
response.Success(c, gin.H{
|
||||||
|
"users": []interface{}{},
|
||||||
|
"total": 0,
|
||||||
|
"active_count": 0,
|
||||||
|
"disabled_count": 0,
|
||||||
|
"banned_count": 0,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -431,12 +454,12 @@ func handleGetUsers(c *gin.Context) {
|
|||||||
pageSize := c.DefaultQuery("page_size", "20")
|
pageSize := c.DefaultQuery("page_size", "20")
|
||||||
|
|
||||||
var total int64
|
var total int64
|
||||||
database.DB.Model(&model.AppUser{}).Where("application_id IN ?", appIDs).Count(&total)
|
database.DB.Model(&model.AppUser{}).Where("id IN ?", appUserIDs).Count(&total)
|
||||||
|
|
||||||
var activeCount, disabledCount, bannedCount int64
|
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("id IN ? AND status = ?", appUserIDs, "active").Count(&activeCount)
|
||||||
database.DB.Model(&model.AppUser{}).Where("application_id IN ? AND status = ?", appIDs, "disabled").Count(&disabledCount)
|
database.DB.Model(&model.AppUser{}).Where("id IN ? AND status = ?", appUserIDs, "disabled").Count(&disabledCount)
|
||||||
database.DB.Model(&model.AppUser{}).Where("application_id IN ? AND status = ?", appIDs, "banned").Count(&bannedCount)
|
database.DB.Model(&model.AppUser{}).Where("id IN ? AND status = ?", appUserIDs, "banned").Count(&bannedCount)
|
||||||
|
|
||||||
var users []model.AppUser
|
var users []model.AppUser
|
||||||
offset := 0
|
offset := 0
|
||||||
@@ -449,7 +472,7 @@ func handleGetUsers(c *gin.Context) {
|
|||||||
limit = pageSizeInt
|
limit = pageSizeInt
|
||||||
}
|
}
|
||||||
|
|
||||||
database.DB.Where("application_id IN ?", appIDs).Order("created_at DESC").Limit(limit).Offset(offset).Find(&users)
|
database.DB.Where("id IN ?", appUserIDs).Order("created_at DESC").Limit(limit).Offset(offset).Find(&users)
|
||||||
|
|
||||||
response.Success(c, gin.H{
|
response.Success(c, gin.H{
|
||||||
"users": users,
|
"users": users,
|
||||||
@@ -710,7 +733,7 @@ func handleExportCards(c *gin.Context) {
|
|||||||
for _, card := range cards {
|
for _, card := range cards {
|
||||||
ct := cardTypeMap[card.CardTypeID]
|
ct := cardTypeMap[card.CardTypeID]
|
||||||
app := appMap[card.ApplicationID]
|
app := appMap[card.ApplicationID]
|
||||||
statusMap := map[string]string{"unused": "未使用", "used": "已使用", "expired": "已过期", "disabled": "已禁用"}
|
statusMap := map[string]string{"unused": "未使用", "used": "已使用", "expired": "已过期", "banned": "已禁用"}
|
||||||
statusText := statusMap[card.Status]
|
statusText := statusMap[card.Status]
|
||||||
if statusText == "" {
|
if statusText == "" {
|
||||||
statusText = card.Status
|
statusText = card.Status
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export function getColumns(t: Composer['t']): ColumnDef<Card>[] {
|
|||||||
unused: { label: t('agent.cards.status.unused'), variant: 'default' },
|
unused: { label: t('agent.cards.status.unused'), variant: 'default' },
|
||||||
used: { label: t('agent.cards.status.used'), variant: 'secondary' },
|
used: { label: t('agent.cards.status.used'), variant: 'secondary' },
|
||||||
expired: { label: t('agent.cards.status.expired'), variant: 'destructive' },
|
expired: { label: t('agent.cards.status.expired'), variant: 'destructive' },
|
||||||
disabled: { label: t('agent.cards.status.disabled'), variant: 'secondary' },
|
banned: { label: t('agent.cards.status.banned'), variant: 'destructive' },
|
||||||
}
|
}
|
||||||
const { label, variant } = statusMap[status] || { label: status || '-', variant: 'secondary' }
|
const { label, variant } = statusMap[status] || { label: status || '-', variant: 'secondary' }
|
||||||
return h(Badge, { variant }, () => label)
|
return h(Badge, { variant }, () => label)
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ const total = ref(0)
|
|||||||
const unusedCount = ref(0)
|
const unusedCount = ref(0)
|
||||||
const usedCount = ref(0)
|
const usedCount = ref(0)
|
||||||
const expiredCount = ref(0)
|
const expiredCount = ref(0)
|
||||||
const disabledCount = ref(0)
|
const bannedCount = ref(0)
|
||||||
const currentPage = ref(1)
|
const currentPage = ref(1)
|
||||||
const pageSize = ref(20)
|
const pageSize = ref(20)
|
||||||
const tableRef = ref()
|
const tableRef = ref()
|
||||||
@@ -74,7 +74,7 @@ const statusOptions = computed(() => [
|
|||||||
{ label: t('agent.cards.status.unused'), value: 'unused' },
|
{ label: t('agent.cards.status.unused'), value: 'unused' },
|
||||||
{ label: t('agent.cards.status.used'), value: 'used' },
|
{ label: t('agent.cards.status.used'), value: 'used' },
|
||||||
{ label: t('agent.cards.status.expired'), value: 'expired' },
|
{ label: t('agent.cards.status.expired'), value: 'expired' },
|
||||||
{ label: t('agent.cards.status.disabled'), value: 'disabled' },
|
{ label: t('agent.cards.status.banned'), value: 'banned' },
|
||||||
])
|
])
|
||||||
|
|
||||||
const serverPagination = computed(() => ({
|
const serverPagination = computed(() => ({
|
||||||
@@ -152,7 +152,7 @@ async function fetchCards() {
|
|||||||
unusedCount.value = data.unused_count || 0
|
unusedCount.value = data.unused_count || 0
|
||||||
usedCount.value = data.used_count || 0
|
usedCount.value = data.used_count || 0
|
||||||
expiredCount.value = data.expired_count || 0
|
expiredCount.value = data.expired_count || 0
|
||||||
disabledCount.value = data.disabled_count || 0
|
bannedCount.value = data.banned_count || data.disabled_count || 0
|
||||||
} else if (Array.isArray(data)) {
|
} else if (Array.isArray(data)) {
|
||||||
cards.value = data
|
cards.value = data
|
||||||
total.value = data.length
|
total.value = data.length
|
||||||
@@ -241,7 +241,7 @@ onMounted(async () => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-5">
|
||||||
<UiCard>
|
<UiCard>
|
||||||
<UiCardHeader class="flex flex-row items-center justify-between pb-2 space-y-0">
|
<UiCardHeader class="flex flex-row items-center justify-between pb-2 space-y-0">
|
||||||
<UiCardTitle class="text-sm font-medium">
|
<UiCardTitle class="text-sm font-medium">
|
||||||
@@ -297,6 +297,20 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
</UiCardContent>
|
</UiCardContent>
|
||||||
</UiCard>
|
</UiCard>
|
||||||
|
|
||||||
|
<UiCard>
|
||||||
|
<UiCardHeader class="flex flex-row items-center justify-between pb-2 space-y-0">
|
||||||
|
<UiCardTitle class="text-sm font-medium">
|
||||||
|
{{ t('agent.cards.banned') }}
|
||||||
|
</UiCardTitle>
|
||||||
|
<Key class="size-4 text-muted-foreground" />
|
||||||
|
</UiCardHeader>
|
||||||
|
<UiCardContent>
|
||||||
|
<div class="text-2xl font-bold">
|
||||||
|
{{ bannedCount }}
|
||||||
|
</div>
|
||||||
|
</UiCardContent>
|
||||||
|
</UiCard>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UiCard>
|
<UiCard>
|
||||||
|
|||||||
@@ -2819,7 +2819,7 @@
|
|||||||
"unused": "Unused",
|
"unused": "Unused",
|
||||||
"used": "Used",
|
"used": "Used",
|
||||||
"expired": "Expired",
|
"expired": "Expired",
|
||||||
"disabled": "Disabled",
|
"banned": "Banned",
|
||||||
"totalCards": "Total Cards",
|
"totalCards": "Total Cards",
|
||||||
"searchPlaceholder": "Search card code, app or card type...",
|
"searchPlaceholder": "Search card code, app or card type...",
|
||||||
"exportBtn": "Export",
|
"exportBtn": "Export",
|
||||||
@@ -2840,7 +2840,7 @@
|
|||||||
"unused": "Unused",
|
"unused": "Unused",
|
||||||
"used": "Used",
|
"used": "Used",
|
||||||
"expired": "Expired",
|
"expired": "Expired",
|
||||||
"disabled": "Disabled"
|
"banned": "Banned"
|
||||||
},
|
},
|
||||||
"create": {
|
"create": {
|
||||||
"title": "Generate Cards",
|
"title": "Generate Cards",
|
||||||
|
|||||||
@@ -2820,7 +2820,7 @@
|
|||||||
"unused": "未使用",
|
"unused": "未使用",
|
||||||
"used": "已使用",
|
"used": "已使用",
|
||||||
"expired": "已过期",
|
"expired": "已过期",
|
||||||
"disabled": "已禁用",
|
"banned": "已禁用",
|
||||||
"totalCards": "卡密总数",
|
"totalCards": "卡密总数",
|
||||||
"searchPlaceholder": "搜索卡号、应用或卡类...",
|
"searchPlaceholder": "搜索卡号、应用或卡类...",
|
||||||
"exportBtn": "导出",
|
"exportBtn": "导出",
|
||||||
@@ -2841,7 +2841,7 @@
|
|||||||
"unused": "未使用",
|
"unused": "未使用",
|
||||||
"used": "已使用",
|
"used": "已使用",
|
||||||
"expired": "已过期",
|
"expired": "已过期",
|
||||||
"disabled": "已禁用"
|
"banned": "已禁用"
|
||||||
},
|
},
|
||||||
"create": {
|
"create": {
|
||||||
"title": "生成卡密",
|
"title": "生成卡密",
|
||||||
|
|||||||
Reference in New Issue
Block a user