feat: add superadmin wallet view
This commit is contained in:
@@ -203,6 +203,36 @@ type UserItem struct {
|
|||||||
JoinedTenantCount int64 `json:"joined_tenant_count"`
|
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 {
|
type UserStatistics struct {
|
||||||
// Status 用户状态枚举。
|
// Status 用户状态枚举。
|
||||||
Status consts.UserStatus `json:"status"`
|
Status consts.UserStatus `json:"status"`
|
||||||
|
|||||||
@@ -185,6 +185,11 @@ func (r *Routes) Register(router fiber.Router) {
|
|||||||
PathParam[int64]("id"),
|
PathParam[int64]("id"),
|
||||||
Query[dto.SuperUserTenantListFilter]("filter"),
|
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")
|
r.log.Debugf("Registering route: Get /super/v1/users/statistics -> users.Statistics")
|
||||||
router.Get("/super/v1/users/statistics"[len(r.Path()):], DataFunc0(
|
router.Get("/super/v1/users/statistics"[len(r.Path()):], DataFunc0(
|
||||||
r.users.Statistics,
|
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)
|
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
|
// List user tenants
|
||||||
//
|
//
|
||||||
// @Router /super/v1/users/:id<int>/tenants [get]
|
// @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
|
}, 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 {
|
func (s *super) UpdateUserStatus(ctx context.Context, id int64, form *super_dto.UserStatusUpdateForm) error {
|
||||||
tbl, q := models.UserQuery.QueryContext(ctx)
|
tbl, q := models.UserQuery.QueryContext(ctx)
|
||||||
_, err := q.Where(tbl.ID.Eq(id)).Update(tbl.Status, consts.UserStatus(form.Status))
|
_, 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": {
|
"/super/v1/withdrawals": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "List withdrawal orders across tenants",
|
"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": {
|
"dto.SuperWithdrawalRejectForm": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"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": {
|
"/super/v1/withdrawals": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "List withdrawal orders across tenants",
|
"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": {
|
"dto.SuperWithdrawalRejectForm": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
|||||||
@@ -1280,6 +1280,51 @@ definitions:
|
|||||||
description: VerifiedAt 实名认证时间(RFC3339)。
|
description: VerifiedAt 实名认证时间(RFC3339)。
|
||||||
type: string
|
type: string
|
||||||
type: object
|
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:
|
dto.SuperWithdrawalRejectForm:
|
||||||
properties:
|
properties:
|
||||||
reason:
|
reason:
|
||||||
@@ -2815,6 +2860,28 @@ paths:
|
|||||||
summary: List user tenants
|
summary: List user tenants
|
||||||
tags:
|
tags:
|
||||||
- User
|
- 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:
|
/super/v1/users/statistics:
|
||||||
get:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
|
|||||||
@@ -33,12 +33,12 @@
|
|||||||
### 2.5 用户管理 `/superadmin/users`
|
### 2.5 用户管理 `/superadmin/users`
|
||||||
- 状态:**部分完成**
|
- 状态:**部分完成**
|
||||||
- 已有:用户列表、角色/状态变更、统计。
|
- 已有:用户列表、角色/状态变更、统计。
|
||||||
- 缺口:钱包明细、实名认证详情、通知/优惠券明细、充值记录等超管视图接口。
|
- 缺口:实名认证详情、通知/优惠券明细、充值记录等超管视图接口。
|
||||||
|
|
||||||
### 2.6 用户详情 `/superadmin/users/:userID`
|
### 2.6 用户详情 `/superadmin/users/:userID`
|
||||||
- 状态:**部分完成**
|
- 状态:**部分完成**
|
||||||
- 已有:用户资料、租户关系、订单查询。
|
- 已有:用户资料、租户关系、订单查询、钱包余额与流水。
|
||||||
- 缺口:钱包流水、通知、优惠券、收藏/点赞等详情。
|
- 缺口:通知、优惠券、收藏/点赞等详情。
|
||||||
|
|
||||||
### 2.7 内容治理 `/superadmin/contents`
|
### 2.7 内容治理 `/superadmin/contents`
|
||||||
- 状态:**部分完成**
|
- 状态:**部分完成**
|
||||||
@@ -82,8 +82,8 @@
|
|||||||
|
|
||||||
## 3) `/super/v1` 接口覆盖度概览
|
## 3) `/super/v1` 接口覆盖度概览
|
||||||
|
|
||||||
- **已具备**:Auth、Tenants、Users、Contents、Orders、Withdrawals、Reports、Coupons(列表)、Creators(列表)。
|
- **已具备**:Auth、Tenants、Users(含钱包)、Contents、Orders、Withdrawals、Reports、Coupons(列表)、Creators(列表)。
|
||||||
- **缺失/待补**:资产治理、通知中心、用户钱包/优惠券/通知明细、创作者成员审核、优惠券发放与冻结。
|
- **缺失/待补**:资产治理、通知中心、用户优惠券/通知明细、创作者成员审核、优惠券发放与冻结。
|
||||||
|
|
||||||
## 4) 建议的下一步(按优先级)
|
## 4) 建议的下一步(按优先级)
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,10 @@ export const UserService = {
|
|||||||
if (!userID) throw new Error('userID is required');
|
if (!userID) throw new Error('userID is required');
|
||||||
return requestJson(`/super/v1/users/${userID}`);
|
return requestJson(`/super/v1/users/${userID}`);
|
||||||
},
|
},
|
||||||
|
async getUserWallet(userID) {
|
||||||
|
if (!userID) throw new Error('userID is required');
|
||||||
|
return requestJson(`/super/v1/users/${userID}/wallet`);
|
||||||
|
},
|
||||||
async listUserTenants(userID, { page, limit, tenant_id, code, name, role, status, created_at_from, created_at_to } = {}) {
|
async listUserTenants(userID, { page, limit, tenant_id, code, name, role, status, created_at_from, created_at_to } = {}) {
|
||||||
if (!userID) throw new Error('userID is required');
|
if (!userID) throw new Error('userID is required');
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ const userID = computed(() => Number(route.params.userID));
|
|||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const user = ref(null);
|
const user = ref(null);
|
||||||
|
|
||||||
|
const wallet = ref(null);
|
||||||
|
const walletLoading = ref(false);
|
||||||
|
|
||||||
const tabValue = ref('owned');
|
const tabValue = ref('owned');
|
||||||
|
|
||||||
function formatDate(value) {
|
function formatDate(value) {
|
||||||
@@ -31,6 +34,25 @@ function formatCny(amountInCents) {
|
|||||||
return new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY' }).format(amount);
|
return new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY' }).format(amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatWalletFlow(value) {
|
||||||
|
if (value === 'income') return '收入';
|
||||||
|
if (value === 'expense') return '支出';
|
||||||
|
return value || '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatOrderType(value) {
|
||||||
|
switch (value) {
|
||||||
|
case 'content_purchase':
|
||||||
|
return '内容购买';
|
||||||
|
case 'recharge':
|
||||||
|
return '充值';
|
||||||
|
case 'withdrawal':
|
||||||
|
return '提现';
|
||||||
|
default:
|
||||||
|
return value || '-';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getStatusSeverity(status) {
|
function getStatusSeverity(status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'active':
|
case 'active':
|
||||||
@@ -65,6 +87,19 @@ async function loadUser() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadWallet() {
|
||||||
|
const id = userID.value;
|
||||||
|
if (!id || Number.isNaN(id)) return;
|
||||||
|
walletLoading.value = true;
|
||||||
|
try {
|
||||||
|
wallet.value = await UserService.getUserWallet(id);
|
||||||
|
} catch (error) {
|
||||||
|
toast.add({ severity: 'error', summary: '加载失败', detail: error?.message || '无法加载钱包信息', life: 4000 });
|
||||||
|
} finally {
|
||||||
|
walletLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const statusDialogVisible = ref(false);
|
const statusDialogVisible = ref(false);
|
||||||
const statusLoading = ref(false);
|
const statusLoading = ref(false);
|
||||||
const statusOptionsLoading = ref(false);
|
const statusOptionsLoading = ref(false);
|
||||||
@@ -237,6 +272,7 @@ watch(
|
|||||||
ownedTenantsPage.value = 1;
|
ownedTenantsPage.value = 1;
|
||||||
joinedTenantsPage.value = 1;
|
joinedTenantsPage.value = 1;
|
||||||
loadUser();
|
loadUser();
|
||||||
|
loadWallet();
|
||||||
loadOwnedTenants();
|
loadOwnedTenants();
|
||||||
loadJoinedTenants();
|
loadJoinedTenants();
|
||||||
},
|
},
|
||||||
@@ -306,6 +342,7 @@ onMounted(() => {
|
|||||||
<TabList>
|
<TabList>
|
||||||
<Tab value="owned">拥有的租户</Tab>
|
<Tab value="owned">拥有的租户</Tab>
|
||||||
<Tab value="joined">加入的租户</Tab>
|
<Tab value="joined">加入的租户</Tab>
|
||||||
|
<Tab value="wallet">钱包</Tab>
|
||||||
</TabList>
|
</TabList>
|
||||||
<TabPanels>
|
<TabPanels>
|
||||||
<TabPanel value="owned">
|
<TabPanel value="owned">
|
||||||
@@ -453,6 +490,54 @@ onMounted(() => {
|
|||||||
</DataTable>
|
</DataTable>
|
||||||
</div>
|
</div>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
<TabPanel value="wallet">
|
||||||
|
<div class="flex flex-col gap-4">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex items-center gap-6">
|
||||||
|
<div>
|
||||||
|
<div class="text-sm text-muted-color">可用余额</div>
|
||||||
|
<div class="font-medium">{{ formatCny(wallet?.balance) }}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="text-sm text-muted-color">冻结余额</div>
|
||||||
|
<div class="font-medium">{{ formatCny(wallet?.balance_frozen) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button label="刷新" icon="pi pi-refresh" severity="secondary" @click="loadWallet" :disabled="walletLoading" />
|
||||||
|
</div>
|
||||||
|
<DataTable :value="wallet?.transactions || []" dataKey="id" :loading="walletLoading" scrollable scrollHeight="420px" responsiveLayout="scroll">
|
||||||
|
<Column field="id" header="订单ID" style="min-width: 8rem" />
|
||||||
|
<Column field="order_type" header="类型" style="min-width: 10rem">
|
||||||
|
<template #body="{ data }">
|
||||||
|
{{ formatOrderType(data.order_type) }}
|
||||||
|
</template>
|
||||||
|
</Column>
|
||||||
|
<Column field="type" header="流向" style="min-width: 8rem">
|
||||||
|
<template #body="{ data }">
|
||||||
|
{{ formatWalletFlow(data.type) }}
|
||||||
|
</template>
|
||||||
|
</Column>
|
||||||
|
<Column field="amount" header="金额" style="min-width: 10rem">
|
||||||
|
<template #body="{ data }">
|
||||||
|
{{ formatCny(data.amount) }}
|
||||||
|
</template>
|
||||||
|
</Column>
|
||||||
|
<Column header="租户" style="min-width: 16rem">
|
||||||
|
<template #body="{ data }">
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="font-medium">{{ data.tenant_name || '平台/系统' }}</span>
|
||||||
|
<span class="text-xs text-muted-color">Code: {{ data.tenant_code || '-' }} / ID: {{ data.tenant_id ?? '-' }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Column>
|
||||||
|
<Column field="date" header="时间" style="min-width: 14rem">
|
||||||
|
<template #body="{ data }">
|
||||||
|
{{ formatDate(data.date) }}
|
||||||
|
</template>
|
||||||
|
</Column>
|
||||||
|
</DataTable>
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
</TabPanels>
|
</TabPanels>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user