fix(member):修复成员列表导出字段与表格不一致的问题

This commit is contained in:
qian committed 2026-08-14 23:59:05 +08:00
1 parent a073d6b272
commit 0e3cecf144
1 file changed
+111 -14
+111 -14
View File
@@ -5,6 +5,7 @@ import (
"crypto/rand"
"encoding/hex"
"fmt"
"strconv"
"strings"
"time"
@@ -1065,7 +1066,35 @@ func (s *sTenant) ListMemberApiKeys(ctx context.Context, req *v1.TenantMemberApi
}, nil
}
// 成员导出显示文案,与前端成员列表页保持一致
var memberRoleLabels = map[string]string{
"owner": "所有者",
"admin": "管理员",
"member": "成员",
}
var memberStatusLabels = map[string]string{
"active": "已激活",
"invited": "已邀请",
"disabled": "已禁用",
}
var memberPeriodLabels = map[string]string{
"day": "按天",
"week": "按周",
"month": "按月",
}
// formatMemberUSD 格式化金额为 $ 前缀、最多两位小数(去掉末尾 0),与前端 formatMoney(precision: 2) 输出一致
func formatMemberUSD(v float64) string {
s := strconv.FormatFloat(v, 'f', 2, 64)
s = strings.TrimRight(s, "0")
s = strings.TrimSuffix(s, ".")
return "$" + s
}
// ExportMembers exports the tenant member list as CSV or Excel.
// 导出列与成员列表页表格保持一致(用户/角色/状态/额度限制/可用模型/本月消费/加入时间/最后更新)。
func (s *sTenant) ExportMembers(ctx context.Context, req *v1.TenantMemberExportReq) (*v1.TenantMemberExportRes, error) {
role := middleware.GetUserRole(ctx)
if role != "owner" && role != "admin" {
@@ -1077,13 +1106,16 @@ func (s *sTenant) ExportMembers(ctx context.Context, req *v1.TenantMemberExportR
tenantID := middleware.GetTenantID(ctx)
columns := []export.Column{
{Field: "id", Header: "ID"},
{Field: "username", Header: "用户名"},
{Field: "email", Header: "邮箱"},
{Field: "display_name", Header: "显示名称"},
{Field: "email", Header: "邮箱"},
{Field: "role", Header: "角色"},
{Field: "status", Header: "状态"},
{Field: "created_at", Header: "创建时间"},
{Field: "quota", Header: "额度限制"},
{Field: "model_count", Header: "可用模型"},
{Field: "month_cost", Header: "本月消费(USD)"},
{Field: "created_at", Header: "加入时间"},
{Field: "updated_at", Header: "最后更新"},
}
config := export.Config{
@@ -1106,27 +1138,92 @@ func (s *sTenant) ExportMembers(ctx context.Context, req *v1.TenantMemberExportR
}
var users []struct {
Id int64 `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
DisplayName string `json:"display_name"`
Role string `json:"role"`
Status string `json:"status"`
CreatedAt string `json:"created_at"`
Id int64 `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
DisplayName string `json:"display_name"`
Role string `json:"role"`
Status string `json:"status"`
CreatedAt string `json:"created_at"`
QuotaType string `json:"quota_type"`
QuotaLimit float64 `json:"quota_limit"`
QuotaPeriod string `json:"quota_period"`
UpdatedAt string `json:"updated_at"`
}
err := model.OrderDesc("id").Limit(1000).Offset(offset).Scan(&users)
if err = common.IgnoreScanNoRows(err); err != nil {
return
}
// 与列表页口径一致:批量聚合模型范围与本月消费,避免逐行 N+1 查询
userIds := make([]int64, len(users))
for i, u := range users {
userIds[i] = u.Id
}
modelCountMap := make(map[int64]int)
hasScopeMap := make(map[int64]bool)
monthCostMap := make(map[int64]float64)
if len(userIds) > 0 {
var scopeRows []struct {
UserId int64 `json:"user_id"`
ModelId int64 `json:"model_id"`
}
if err = dao.TntMemberModelScopes.Ctx(ctx).
Where("tenant_id", tenantID).
Where("user_id", userIds).
Scan(&scopeRows); err == nil {
for _, r := range scopeRows {
hasScopeMap[r.UserId] = true
if r.ModelId > 0 {
modelCountMap[r.UserId]++
}
}
}
monthStart := time.Now().Format("2006-01") + "-01 00:00:00"
var costRows []struct {
UserId int64 `json:"user_id"`
Cost float64 `json:"cost"`
}
if err = dao.BilUsageLogs.Ctx(ctx).
Fields("user_id, COALESCE(SUM(total_cost), 0) as cost").
Where("tenant_id", tenantID).
Where("user_id", userIds).
Where("created_at >= ?", monthStart).
Group("user_id").
Scan(&costRows); err == nil {
for _, c := range costRows {
monthCostMap[c.UserId] = c.Cost
}
}
}
for _, u := range users {
// 额度限制:不限制 / 金额(周期性追加周期文案),与列表页渲染一致
quota := "不限制"
if u.QuotaType != "" && u.QuotaType != "none" {
quota = formatMemberUSD(u.QuotaLimit)
if u.QuotaType == "periodic" && u.QuotaPeriod != "" {
quota += "(" + memberPeriodLabels[u.QuotaPeriod] + ")"
}
}
// 可用模型:无任何范围记录 = 不限,否则输出授权数量
modelCount := "不限"
if hasScopeMap[u.Id] {
modelCount = strconv.Itoa(modelCountMap[u.Id])
}
if !yield(map[string]any{
"id": u.Id,
"username": u.Username,
"email": u.Email,
"display_name": u.DisplayName,
"role": u.Role,
"status": u.Status,
"email": u.Email,
"role": memberRoleLabels[u.Role],
"status": memberStatusLabels[u.Status],
"quota": quota,
"model_count": modelCount,
"month_cost": formatMemberUSD(monthCostMap[u.Id]),
"created_at": u.CreatedAt,
"updated_at": u.UpdatedAt,
}) {
return
}