feat(bill):优化计费功能
This commit is contained in:
1 parent
386b596fb5
commit
5b7085f3b7
7 files changed
+7
-115
No files matched your search
@@ -31,7 +31,6 @@ type ITenantV1 interface {
|
||||
TenantSessionList(ctx context.Context, req *v1.TenantSessionListReq) (res *v1.TenantSessionListRes, err error)
|
||||
TenantRevokeSession(ctx context.Context, req *v1.TenantRevokeSessionReq) (res *v1.TenantRevokeSessionRes, err error)
|
||||
TenantUsageLogs(ctx context.Context, req *v1.TenantUsageLogsReq) (res *v1.TenantUsageLogsRes, err error)
|
||||
TenantBillingRecords(ctx context.Context, req *v1.TenantBillingRecordsReq) (res *v1.TenantBillingRecordsRes, err error)
|
||||
TenantUsageLogsExport(ctx context.Context, req *v1.TenantUsageLogsExportReq) (res *v1.TenantUsageLogsExportRes, err error)
|
||||
TenantRequestClosure(ctx context.Context, req *v1.TenantRequestClosureReq) (res *v1.TenantRequestClosureRes, err error)
|
||||
TenantCancelClosure(ctx context.Context, req *v1.TenantCancelClosureReq) (res *v1.TenantCancelClosureRes, err error)
|
||||
|
||||
@@ -23,19 +23,6 @@ type TenantUsageLogsRes struct {
|
||||
PageSize int `json:"page_size"`
|
||||
}
|
||||
|
||||
type TenantBillingRecordsReq struct {
|
||||
g.Meta `path:"/billing-records" method:"get" mime:"json" tags:"租户控制台-用量" summary:"账单记录"`
|
||||
Page int `json:"page" in:"query" d:"1"`
|
||||
PageSize int `json:"page_size" in:"query" d:"20"`
|
||||
}
|
||||
|
||||
type TenantBillingRecordsRes struct {
|
||||
List []map[string]any `json:"list"`
|
||||
Total int `json:"total"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
}
|
||||
|
||||
// TenantUsageLogsExportReq 导出用量日志请求
|
||||
type TenantUsageLogsExportReq struct {
|
||||
g.Meta `path:"/usage-logs/export" method:"get" mime:"json" tags:"租户控制台-用量" summary:"导出用量日志"`
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package tenant
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qianfree/team-api/api/tenant/v1"
|
||||
"github.com/qianfree/team-api/internal/service"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) TenantBillingRecords(ctx context.Context, req *v1.TenantBillingRecordsReq) (res *v1.TenantBillingRecordsRes, err error) {
|
||||
return service.Tenant().BillingRecords(ctx, req)
|
||||
}
|
||||
@@ -147,90 +147,6 @@ func (s *sTenant) WalletTransactions(ctx context.Context, req *v1.TenantWalletTr
|
||||
}, nil
|
||||
}
|
||||
|
||||
// BillingRecords 获取租户计费记录
|
||||
func (s *sTenant) BillingRecords(ctx context.Context, req *v1.TenantBillingRecordsReq) (*v1.TenantBillingRecordsRes, error) {
|
||||
tenantID := ctxTenantID(ctx)
|
||||
userID := ctxUserID(ctx)
|
||||
role := ctxUserRole(ctx)
|
||||
page, pageSize := common.NormalizePagination(req.Page, req.PageSize)
|
||||
|
||||
query := dao.BilRecords.Ctx(ctx).
|
||||
Where("bil_records.tenant_id", tenantID)
|
||||
|
||||
// member 角色只能查看自己的账单记录
|
||||
if role == "member" {
|
||||
query = query.Where("bil_records.user_id", userID)
|
||||
}
|
||||
|
||||
type billingRecordRow struct {
|
||||
Id int64 `json:"id"`
|
||||
ModelName string `json:"model_name"`
|
||||
RelayMode string `json:"relay_mode"`
|
||||
InputTokens int `json:"input_tokens"`
|
||||
OutputTokens int `json:"output_tokens"`
|
||||
InputPrice float64 `json:"input_price"`
|
||||
OutputPrice float64 `json:"output_price"`
|
||||
TotalCost float64 `json:"total_cost"`
|
||||
Currency string `json:"currency"`
|
||||
Status string `json:"status"`
|
||||
SettledAt *gtime.Time `json:"settled_at"`
|
||||
CreatedAt *gtime.Time `json:"created_at"`
|
||||
RequestId string `json:"request_id"`
|
||||
UserId int64 `json:"user_id"`
|
||||
Username string `json:"username"`
|
||||
ProjectId int64 `json:"project_id"`
|
||||
ProjectName string `json:"project_name"`
|
||||
ApiKeyId int64 `json:"api_key_id"`
|
||||
ApiKeyName string `json:"api_key_name"`
|
||||
}
|
||||
|
||||
var records []billingRecordRow
|
||||
var err error
|
||||
var total int
|
||||
err = query.Fields("bil_records.id, bil_records.model_name, bil_records.relay_mode, bil_records.input_tokens, bil_records.output_tokens, bil_records.input_price, bil_records.output_price, bil_records.total_cost, bil_records.currency, bil_records.status, bil_records.settled_at, bil_records.created_at, bil_records.request_id, bil_records.user_id, COALESCE(tu.username, '') AS username, bil_records.api_key_id, COALESCE(ak.name, '') AS api_key_name").
|
||||
LeftJoin("tnt_users tu", "bil_records.user_id = tu.id AND bil_records.tenant_id = tu.tenant_id").
|
||||
LeftJoin("api_keys ak", "bil_records.api_key_id = ak.id AND bil_records.tenant_id = ak.tenant_id").
|
||||
OrderDesc("bil_records.created_at").
|
||||
Page(page, pageSize).
|
||||
ScanAndCount(&records, &total, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if records == nil {
|
||||
records = []billingRecordRow{}
|
||||
}
|
||||
|
||||
list := make([]map[string]any, 0, len(records))
|
||||
for _, r := range records {
|
||||
list = append(list, map[string]any{
|
||||
"id": r.Id,
|
||||
"model_name": r.ModelName,
|
||||
"relay_mode": r.RelayMode,
|
||||
"input_tokens": r.InputTokens,
|
||||
"output_tokens": r.OutputTokens,
|
||||
"input_price": r.InputPrice,
|
||||
"output_price": r.OutputPrice,
|
||||
"total_cost": r.TotalCost,
|
||||
"currency": r.Currency,
|
||||
"status": r.Status,
|
||||
"settled_at": r.SettledAt,
|
||||
"created_at": r.CreatedAt,
|
||||
"request_id": r.RequestId,
|
||||
"user_id": r.UserId,
|
||||
"username": r.Username,
|
||||
"api_key_id": r.ApiKeyId,
|
||||
"api_key_name": r.ApiKeyName,
|
||||
})
|
||||
}
|
||||
|
||||
return &v1.TenantBillingRecordsRes{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UsageLogs 获取租户用量日志
|
||||
func (s *sTenant) UsageLogs(ctx context.Context, req *v1.TenantUsageLogsReq) (*v1.TenantUsageLogsRes, error) {
|
||||
tenantID := ctxTenantID(ctx)
|
||||
|
||||
@@ -57,8 +57,6 @@ type (
|
||||
Wallet(ctx context.Context, req *v1.TenantWalletReq) (*v1.TenantWalletRes, error)
|
||||
// WalletTransactions 获取租户钱包流水
|
||||
WalletTransactions(ctx context.Context, req *v1.TenantWalletTransactionsReq) (*v1.TenantWalletTransactionsRes, error)
|
||||
// BillingRecords 获取租户计费记录
|
||||
BillingRecords(ctx context.Context, req *v1.TenantBillingRecordsReq) (*v1.TenantBillingRecordsRes, error)
|
||||
// UsageLogs 获取租户用量日志
|
||||
UsageLogs(ctx context.Context, req *v1.TenantUsageLogsReq) (*v1.TenantUsageLogsRes, error)
|
||||
// ExportUsageLogs exports the tenant usage logs as CSV or Excel.
|
||||
|
||||
@@ -139,8 +139,8 @@ const tenantRoutes: RouteRecordRaw[] = [
|
||||
component: () => import('@/views/finance/TransactionsPage.vue'),
|
||||
meta: { title: '交易记录', icon: 'document', sort: 41, roles: ['owner', 'admin'] },
|
||||
},
|
||||
{
|
||||
path: 'orders',
|
||||
{
|
||||
path: 'orders',
|
||||
name: 'TenantOrders',
|
||||
component: () => import('@/views/finance/OrdersPage.vue'),
|
||||
meta: { title: '订单记录', icon: 'receipt', sort: 42, roles: ['owner', 'admin'] },
|
||||
|
||||
@@ -772,7 +772,7 @@ onMounted(() => {
|
||||
</div>
|
||||
|
||||
<!-- 结算明细 -->
|
||||
<div v-if="detailLog.pre_deduct_amount > 0 || detailLog.refund_amount > 0 || detailLog.supplement_amount > 0">
|
||||
<div v-if="detailLog.pre_deduct_amount > 0 || detailLog.refund_amount > 0 || detailLog.supplement_amount > 0 || detailLog.settled_at">
|
||||
<h4 class="text-sm font-semibold text-gray-700 mb-3 flex items-center gap-2">
|
||||
<Icon name="refresh" size="sm" class="text-primary-500" />
|
||||
结算明细
|
||||
@@ -790,6 +790,10 @@ onMounted(() => {
|
||||
<span class="text-gray-500">补扣金额</span>
|
||||
<span class="text-amber-600">{{ formatCost(detailLog.supplement_amount) }}</span>
|
||||
</div>
|
||||
<div v-if="detailLog.settled_at" class="flex items-center justify-between">
|
||||
<span class="text-gray-500">结算时间</span>
|
||||
<span>{{ formatTime(detailLog.settled_at) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in new issue
Block a user