更新日志展示

This commit is contained in:
2026-07-02 14:23:30 +08:00
parent 39b149b206
commit 12070ff4fd
5 changed files with 64 additions and 1 deletions
@@ -67,6 +67,13 @@ func (h *AdminReadHandler) Logs(c *gin.Context) {
c.JSON(http.StatusInternalServerError, gin.H{"detail": "failed to load logs"})
return
}
// Resolve account_id -> account label (email) so the log table can show which
// provider account fulfilled each generation under the user.
accountByID, err := h.admin.AccountNameMap(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"detail": "failed to load logs"})
return
}
out := make([]gin.H, 0, len(items))
for _, item := range items {
var userName any
@@ -77,6 +84,14 @@ func (h *AdminReadHandler) Logs(c *gin.Context) {
} else {
userName = item.UserID
}
var accountName any
if item.AccountID != "" {
if label, ok := accountByID[item.AccountID]; ok {
accountName = label
} else {
accountName = item.AccountID
}
}
out = append(out, gin.H{
"id": item.ID,
"ts": item.TS.Unix(),
@@ -92,6 +107,8 @@ func (h *AdminReadHandler) Logs(c *gin.Context) {
"source": item.Source,
"user_id": emptyStringNil(item.UserID),
"user_name": userName,
"account_id": emptyStringNil(item.AccountID),
"account": accountName,
"cost": item.Cost,
"elapsed_ms": item.ElapsedMS,
"file": emptyStringNil(item.File),
@@ -218,6 +218,13 @@ func (h *UserGenerationHandler) Logs(c *gin.Context) {
c.JSON(http.StatusInternalServerError, gin.H{"detail": "failed to load logs"})
return
}
// Resolve account_id -> account label so the log table can show which
// provider account fulfilled each generation under the user.
accountByID, err := h.admin.AccountNameMap(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"detail": "failed to load logs"})
return
}
out := make([]gin.H, 0, len(items))
for _, item := range items {
@@ -229,6 +236,14 @@ func (h *UserGenerationHandler) Logs(c *gin.Context) {
} else {
userName = item.UserID
}
var accountName any
if item.AccountID != "" {
if label, ok := accountByID[item.AccountID]; ok {
accountName = label
} else {
accountName = item.AccountID
}
}
out = append(out, gin.H{
"id": item.ID,
"ts": item.TS.Unix(),
@@ -244,6 +259,8 @@ func (h *UserGenerationHandler) Logs(c *gin.Context) {
"source": emptyStringNil(item.Source),
"user_id": emptyStringNil(item.UserID),
"user_name": userName,
"account_id": emptyStringNil(item.AccountID),
"account": accountName,
"cost": item.Cost,
"elapsed_ms": item.ElapsedMS,
"file": emptyStringNil(item.File),
+22
View File
@@ -136,6 +136,28 @@ func (s *AdminReadService) UserNameMap(ctx context.Context) (map[string]string,
return out, nil
}
// AccountNameMap builds a token-account id -> display label lookup (account
// email, else display name, else id) used to annotate log rows with which
// provider account fulfilled each generation (event_logs.account_id).
func (s *AdminReadService) AccountNameMap(ctx context.Context) (map[string]string, error) {
accounts, err := s.tokens.List(ctx)
if err != nil {
return nil, err
}
out := make(map[string]string, len(accounts))
for _, a := range accounts {
label := strings.TrimSpace(a.AccountEmail)
if label == "" {
label = strings.TrimSpace(a.AccountDisplayName)
}
if label == "" {
label = a.ID
}
out[a.ID] = label
}
return out, nil
}
func (s *AdminReadService) Stats(ctx context.Context) (map[string]any, error) {
stats, err := s.events.Stats(ctx)
if err != nil {
+2 -1
View File
@@ -237,7 +237,7 @@ const sourcePill = (s) => ({
<th class="text-center px-4 py-3 font-medium">预览</th>
<th class="text-left px-4 py-3 font-medium">时间</th>
<th class="text-left px-3 py-3 font-medium">状态</th>
<th class="text-left px-3 py-3 font-medium">用户</th>
<th class="text-left px-3 py-3 font-medium">用户 / 账号</th>
<th class="text-left px-3 py-3 font-medium">模型</th>
<th class="text-left px-3 py-3 font-medium">提示词 / 错误</th>
<th class="text-left px-3 py-3 font-medium">参数</th>
@@ -280,6 +280,7 @@ const sourcePill = (s) => ({
</td>
<td class="px-3 py-3.5 align-middle min-w-0">
<div class="text-xs text-white/80 truncate" :title="e.user_name || '匿名'">{{ e.user_name || '匿名' }}</div>
<div v-if="e.account" class="mt-0.5 text-[11px] text-white/45 truncate" :title="e.account">{{ e.account }}</div>
</td>
<td class="px-3 py-3.5 align-middle min-w-0">
<div class="font-mono text-xs text-white/90 truncate" :title="e.model">{{ e.model }}</div>
+6
View File
@@ -194,6 +194,7 @@ const params = (e) => {
<col class="w-16" /> <!-- preview -->
<col class="w-28" /> <!-- time -->
<col class="w-24" /> <!-- status -->
<col class="w-28" /> <!-- user/account -->
<col class="w-36" /> <!-- model -->
<col /> <!-- prompt/error -->
<col class="w-40" /> <!-- params -->
@@ -205,6 +206,7 @@ const params = (e) => {
<th class="text-center px-3 py-3 font-medium">预览</th>
<th class="text-left px-3 py-3 font-medium">时间</th>
<th class="text-left px-3 py-3 font-medium">状态</th>
<th class="text-left px-3 py-3 font-medium">用户 / 账号</th>
<th class="text-left px-3 py-3 font-medium">模型</th>
<th class="text-left px-3 py-3 font-medium">提示词 / 错误</th>
<th class="text-left px-3 py-3 font-medium">参数</th>
@@ -236,6 +238,10 @@ const params = (e) => {
<span class="w-1.5 h-1.5 rounded-full" :class="statusDot(e.status)"></span>{{ statusLabel(e.status) }}
</span>
</td>
<td class="px-3 py-3 align-middle min-w-0">
<div class="text-xs text-slate-700 truncate" :title="e.user_name || '匿名'">{{ e.user_name || '匿名' }}</div>
<div v-if="e.account" class="mt-0.5 text-[11px] text-slate-400 truncate" :title="e.account">{{ e.account }}</div>
</td>
<td class="px-3 py-3 align-middle min-w-0">
<div class="font-mono text-xs text-slate-800 truncate" :title="e.model">{{ e.model }}</div>
<div class="mt-0.5 flex items-center gap-1.5">