feat: add superadmin wallet view
This commit is contained in:
@@ -203,6 +203,36 @@ type UserItem struct {
|
||||
JoinedTenantCount int64 `json:"joined_tenant_count"`
|
||||
}
|
||||
|
||||
type SuperWalletResponse struct {
|
||||
// Balance 账户可用余额(分)。
|
||||
Balance int64 `json:"balance"`
|
||||
// BalanceFrozen 账户冻结余额(分)。
|
||||
BalanceFrozen int64 `json:"balance_frozen"`
|
||||
// Transactions 最近交易记录。
|
||||
Transactions []SuperWalletTransaction `json:"transactions"`
|
||||
}
|
||||
|
||||
type SuperWalletTransaction struct {
|
||||
// ID 订单ID。
|
||||
ID int64 `json:"id"`
|
||||
// OrderType 订单类型。
|
||||
OrderType consts.OrderType `json:"order_type"`
|
||||
// Title 交易标题。
|
||||
Title string `json:"title"`
|
||||
// Amount 交易金额(分)。
|
||||
Amount int64 `json:"amount"`
|
||||
// Type 交易流向(income/expense)。
|
||||
Type string `json:"type"`
|
||||
// Date 交易时间(RFC3339)。
|
||||
Date string `json:"date"`
|
||||
// TenantID 交易所属租户ID(充值为0)。
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
// TenantCode 租户编码。
|
||||
TenantCode string `json:"tenant_code"`
|
||||
// TenantName 租户名称。
|
||||
TenantName string `json:"tenant_name"`
|
||||
}
|
||||
|
||||
type UserStatistics struct {
|
||||
// Status 用户状态枚举。
|
||||
Status consts.UserStatus `json:"status"`
|
||||
|
||||
@@ -185,6 +185,11 @@ func (r *Routes) Register(router fiber.Router) {
|
||||
PathParam[int64]("id"),
|
||||
Query[dto.SuperUserTenantListFilter]("filter"),
|
||||
))
|
||||
r.log.Debugf("Registering route: Get /super/v1/users/:id<int>/wallet -> users.Wallet")
|
||||
router.Get("/super/v1/users/:id<int>/wallet"[len(r.Path()):], DataFunc1(
|
||||
r.users.Wallet,
|
||||
PathParam[int64]("id"),
|
||||
))
|
||||
r.log.Debugf("Registering route: Get /super/v1/users/statistics -> users.Statistics")
|
||||
router.Get("/super/v1/users/statistics"[len(r.Path()):], DataFunc0(
|
||||
r.users.Statistics,
|
||||
|
||||
@@ -43,6 +43,21 @@ func (c *users) Get(ctx fiber.Ctx, id int64) (*dto.UserItem, error) {
|
||||
return services.Super.GetUser(ctx, id)
|
||||
}
|
||||
|
||||
// Get user wallet
|
||||
//
|
||||
// @Router /super/v1/users/:id<int>/wallet [get]
|
||||
// @Summary Get user wallet
|
||||
// @Description Get user wallet balance and transactions
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int64 true "User ID"
|
||||
// @Success 200 {object} dto.SuperWalletResponse
|
||||
// @Bind id path
|
||||
func (c *users) Wallet(ctx fiber.Ctx, id int64) (*dto.SuperWalletResponse, error) {
|
||||
return services.Super.GetUserWallet(ctx, id)
|
||||
}
|
||||
|
||||
// List user tenants
|
||||
//
|
||||
// @Router /super/v1/users/:id<int>/tenants [get]
|
||||
|
||||
@@ -294,6 +294,94 @@ func (s *super) GetUser(ctx context.Context, id int64) (*super_dto.UserItem, err
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *super) GetUserWallet(ctx context.Context, userID int64) (*super_dto.SuperWalletResponse, error) {
|
||||
if userID == 0 {
|
||||
return nil, errorx.ErrBadRequest.WithMsg("用户ID不能为空")
|
||||
}
|
||||
|
||||
// 查询用户余额。
|
||||
userTbl, userQuery := models.UserQuery.QueryContext(ctx)
|
||||
u, err := userQuery.Where(userTbl.ID.Eq(userID)).First()
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, errorx.ErrRecordNotFound
|
||||
}
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
|
||||
// 仅返回最近交易记录,避免超管页面加载过重。
|
||||
orderTbl, orderQuery := models.OrderQuery.QueryContext(ctx)
|
||||
orders, err := orderQuery.
|
||||
Where(orderTbl.UserID.Eq(userID), orderTbl.Status.Eq(consts.OrderStatusPaid)).
|
||||
Order(orderTbl.CreatedAt.Desc()).
|
||||
Limit(20).
|
||||
Find()
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
|
||||
// 补齐订单对应的租户信息。
|
||||
tenantIDMap := make(map[int64]struct{})
|
||||
for _, o := range orders {
|
||||
if o.TenantID > 0 {
|
||||
tenantIDMap[o.TenantID] = struct{}{}
|
||||
}
|
||||
}
|
||||
tenantIDs := make([]int64, 0, len(tenantIDMap))
|
||||
for id := range tenantIDMap {
|
||||
tenantIDs = append(tenantIDs, id)
|
||||
}
|
||||
tenantMap := make(map[int64]*models.Tenant)
|
||||
if len(tenantIDs) > 0 {
|
||||
tenantTbl, tenantQuery := models.TenantQuery.QueryContext(ctx)
|
||||
tenants, err := tenantQuery.Where(tenantTbl.ID.In(tenantIDs...)).Find()
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
for _, tenant := range tenants {
|
||||
tenantMap[tenant.ID] = tenant
|
||||
}
|
||||
}
|
||||
|
||||
txs := make([]super_dto.SuperWalletTransaction, 0, len(orders))
|
||||
for _, o := range orders {
|
||||
txType := "expense"
|
||||
switch o.Type {
|
||||
case consts.OrderTypeRecharge:
|
||||
txType = "income"
|
||||
case consts.OrderTypeWithdrawal:
|
||||
txType = "expense"
|
||||
case consts.OrderTypeContentPurchase:
|
||||
txType = "expense"
|
||||
}
|
||||
|
||||
tenant := tenantMap[o.TenantID]
|
||||
tenantCode := ""
|
||||
tenantName := ""
|
||||
if tenant != nil {
|
||||
tenantCode = tenant.Code
|
||||
tenantName = tenant.Name
|
||||
}
|
||||
txs = append(txs, super_dto.SuperWalletTransaction{
|
||||
ID: o.ID,
|
||||
OrderType: o.Type,
|
||||
Title: o.Type.Description(),
|
||||
Amount: o.AmountPaid,
|
||||
Type: txType,
|
||||
Date: o.CreatedAt.Format(time.RFC3339),
|
||||
TenantID: o.TenantID,
|
||||
TenantCode: tenantCode,
|
||||
TenantName: tenantName,
|
||||
})
|
||||
}
|
||||
|
||||
return &super_dto.SuperWalletResponse{
|
||||
Balance: u.Balance,
|
||||
BalanceFrozen: u.BalanceFrozen,
|
||||
Transactions: txs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *super) UpdateUserStatus(ctx context.Context, id int64, form *super_dto.UserStatusUpdateForm) error {
|
||||
tbl, q := models.UserQuery.QueryContext(ctx)
|
||||
_, err := q.Where(tbl.ID.Eq(id)).Update(tbl.Status, consts.UserStatus(form.Status))
|
||||
|
||||
@@ -1260,6 +1260,39 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/super/v1/users/{id}/wallet": {
|
||||
"get": {
|
||||
"description": "Get user wallet balance and transactions",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"User"
|
||||
],
|
||||
"summary": "Get user wallet",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "User ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.SuperWalletResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/super/v1/withdrawals": {
|
||||
"get": {
|
||||
"description": "List withdrawal orders across tenants",
|
||||
@@ -5954,6 +5987,71 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SuperWalletResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"balance": {
|
||||
"description": "Balance 账户可用余额(分)。",
|
||||
"type": "integer"
|
||||
},
|
||||
"balance_frozen": {
|
||||
"description": "BalanceFrozen 账户冻结余额(分)。",
|
||||
"type": "integer"
|
||||
},
|
||||
"transactions": {
|
||||
"description": "Transactions 最近交易记录。",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.SuperWalletTransaction"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SuperWalletTransaction": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"amount": {
|
||||
"description": "Amount 交易金额(分)。",
|
||||
"type": "integer"
|
||||
},
|
||||
"date": {
|
||||
"description": "Date 交易时间(RFC3339)。",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"description": "ID 订单ID。",
|
||||
"type": "integer"
|
||||
},
|
||||
"order_type": {
|
||||
"description": "OrderType 订单类型。",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/consts.OrderType"
|
||||
}
|
||||
]
|
||||
},
|
||||
"tenant_code": {
|
||||
"description": "TenantCode 租户编码。",
|
||||
"type": "string"
|
||||
},
|
||||
"tenant_id": {
|
||||
"description": "TenantID 交易所属租户ID(充值为0)。",
|
||||
"type": "integer"
|
||||
},
|
||||
"tenant_name": {
|
||||
"description": "TenantName 租户名称。",
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"description": "Title 交易标题。",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "Type 交易流向(income/expense)。",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SuperWithdrawalRejectForm": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
@@ -1254,6 +1254,39 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/super/v1/users/{id}/wallet": {
|
||||
"get": {
|
||||
"description": "Get user wallet balance and transactions",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"User"
|
||||
],
|
||||
"summary": "Get user wallet",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "User ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.SuperWalletResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/super/v1/withdrawals": {
|
||||
"get": {
|
||||
"description": "List withdrawal orders across tenants",
|
||||
@@ -5948,6 +5981,71 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SuperWalletResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"balance": {
|
||||
"description": "Balance 账户可用余额(分)。",
|
||||
"type": "integer"
|
||||
},
|
||||
"balance_frozen": {
|
||||
"description": "BalanceFrozen 账户冻结余额(分)。",
|
||||
"type": "integer"
|
||||
},
|
||||
"transactions": {
|
||||
"description": "Transactions 最近交易记录。",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.SuperWalletTransaction"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SuperWalletTransaction": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"amount": {
|
||||
"description": "Amount 交易金额(分)。",
|
||||
"type": "integer"
|
||||
},
|
||||
"date": {
|
||||
"description": "Date 交易时间(RFC3339)。",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"description": "ID 订单ID。",
|
||||
"type": "integer"
|
||||
},
|
||||
"order_type": {
|
||||
"description": "OrderType 订单类型。",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/consts.OrderType"
|
||||
}
|
||||
]
|
||||
},
|
||||
"tenant_code": {
|
||||
"description": "TenantCode 租户编码。",
|
||||
"type": "string"
|
||||
},
|
||||
"tenant_id": {
|
||||
"description": "TenantID 交易所属租户ID(充值为0)。",
|
||||
"type": "integer"
|
||||
},
|
||||
"tenant_name": {
|
||||
"description": "TenantName 租户名称。",
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"description": "Title 交易标题。",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "Type 交易流向(income/expense)。",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SuperWithdrawalRejectForm": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
@@ -1280,6 +1280,51 @@ definitions:
|
||||
description: VerifiedAt 实名认证时间(RFC3339)。
|
||||
type: string
|
||||
type: object
|
||||
dto.SuperWalletResponse:
|
||||
properties:
|
||||
balance:
|
||||
description: Balance 账户可用余额(分)。
|
||||
type: integer
|
||||
balance_frozen:
|
||||
description: BalanceFrozen 账户冻结余额(分)。
|
||||
type: integer
|
||||
transactions:
|
||||
description: Transactions 最近交易记录。
|
||||
items:
|
||||
$ref: '#/definitions/dto.SuperWalletTransaction'
|
||||
type: array
|
||||
type: object
|
||||
dto.SuperWalletTransaction:
|
||||
properties:
|
||||
amount:
|
||||
description: Amount 交易金额(分)。
|
||||
type: integer
|
||||
date:
|
||||
description: Date 交易时间(RFC3339)。
|
||||
type: string
|
||||
id:
|
||||
description: ID 订单ID。
|
||||
type: integer
|
||||
order_type:
|
||||
allOf:
|
||||
- $ref: '#/definitions/consts.OrderType'
|
||||
description: OrderType 订单类型。
|
||||
tenant_code:
|
||||
description: TenantCode 租户编码。
|
||||
type: string
|
||||
tenant_id:
|
||||
description: TenantID 交易所属租户ID(充值为0)。
|
||||
type: integer
|
||||
tenant_name:
|
||||
description: TenantName 租户名称。
|
||||
type: string
|
||||
title:
|
||||
description: Title 交易标题。
|
||||
type: string
|
||||
type:
|
||||
description: Type 交易流向(income/expense)。
|
||||
type: string
|
||||
type: object
|
||||
dto.SuperWithdrawalRejectForm:
|
||||
properties:
|
||||
reason:
|
||||
@@ -2815,6 +2860,28 @@ paths:
|
||||
summary: List user tenants
|
||||
tags:
|
||||
- User
|
||||
/super/v1/users/{id}/wallet:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Get user wallet balance and transactions
|
||||
parameters:
|
||||
- description: User ID
|
||||
format: int64
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/dto.SuperWalletResponse'
|
||||
summary: Get user wallet
|
||||
tags:
|
||||
- User
|
||||
/super/v1/users/statistics:
|
||||
get:
|
||||
consumes:
|
||||
|
||||
Reference in New Issue
Block a user