feat(admin): 「数据源 & RAG」页做实 —— admin 三 mock 页清零 (P1)

审计 P1「admin 三页纯 mock」最后一页。此前 DatasourcesPage 的 GraphRAG 拓扑图
写死节点、「向量/全文/图谱权重滑块」纯 mock——而且权重概念本身虚构:mcp-go 的
RRF 融合是各路等权的倒排互惠融合(rrfK=60 平滑常数),根本没有"每路占几成"。

- store/datasource_query.go:AllDatasources(全平台 KB + 各库文档数/总字数,
  按 (space_id,name) 关联 doc,WithoutTenant);GET /admin/datasources(含
  SystemCounts 平台计数)。
- DatasourcesPage 重写:保留真实 Embedding 模型配置(ModelManager) + 诚实的
  混合检索管线说明(三路 Milvus/Bleve/Neo4j + RRF 等权融合 k=60,非可调权重) +
  平台计数卡片 + 真知识库清单表。删假滑块+假拓扑(~200行 mock)。

诚实边界:RRF 无每路权重,不摆假滑块;检索参数在 mcp-go 代码中。

live:/admin/datasources 返 34用户/25库/53文档,清单带真实文档数字数;
浏览器渲染全对。tsc+41 vitest 全绿。**admin 三 mock 页(Evals/Guardrails/
Datasources)全部做实。**

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-07-18 12:26:16 +08:00
parent c02ebc7bce
commit e29bc9a91e
5 changed files with 173 additions and 222 deletions
@@ -571,6 +571,16 @@ func (h *Handler) AdminUsage(c *gin.Context) {
c.JSON(http.StatusOK, resp)
}
// AdminDatasources: GET /api/v1/admin/datasources —— 数据源清单(全平台知识库 + 文档数/字数)+ 平台计数。
func (h *Handler) AdminDatasources(c *gin.Context) {
ctx := store.WithoutTenant(c.Request.Context())
users, kbs, docs := h.db.SystemCounts(ctx)
c.JSON(http.StatusOK, gin.H{
"counts": gin.H{"users": users, "kbs": kbs, "docs": docs},
"datasources": h.db.AllDatasources(ctx),
})
}
// AdminEvals: GET /api/v1/admin/evals?days= —— 自动评测观测(趋势 + 计数 + 错题本)。全平台口径。
// 数据来自 sundynix_eval(评测经 JetStream eval 流持久落库);此前该页纯 mock。
func (h *Handler) AdminEvals(c *gin.Context) {
@@ -156,6 +156,7 @@ func New(db *store.Postgres, cache *store.Redis, bus *nats.Bus, blobStore *blob.
admin.GET("/overview", h.AdminOverview) // 系统级聚合:全平台用户/任务/评测/模型态/提示词态/健康
admin.GET("/usage", h.AdminUsage) // 用量/积分/成本:全平台按天趋势 + 租户排行 / 单租户余额
admin.GET("/evals", h.AdminEvals) // 自动评测观测:质量趋势 + 计数 + 错题本(真数据)
admin.GET("/datasources", h.AdminDatasources) // 数据源清单:全平台知识库 + 文档数(真数据)
admin.POST("/migrate-kb-storage", h.MigrateKBStorage) // 增量3:存量 KB 三库 owner/kb→space/kb 重灌(一次性)
admin.GET("/audit", h.AuditList) // 敏感操作审计流(倒序,翻页)
admin.GET("/guardrail-events", h.GuardrailEvents) // 护栏命中安全事件流(倒序,翻页)
@@ -0,0 +1,36 @@
package store
import "context"
// 数据源清单查询(admin「数据源 & RAG」页做实用;此前该页图谱与权重全 mock)。
// 全平台口径 → WithoutTenant。Doc.KB 存的是 KB 名字(非 id),故按 (space_id, name) 关联。
// DatasourceKB 是一条知识库的清单行(含文档数与总字数、租户名)。
type DatasourceKB struct {
ID string `json:"id"`
Name string `json:"name"`
Kind string `json:"kind"`
TenantID string `json:"tenant_id"`
TenantName string `json:"tenant_name"`
Owner string `json:"owner"`
DocCount int64 `json:"doc_count"`
TotalWords int64 `json:"total_words"`
}
// AllDatasources 列出全平台知识库 + 各库文档数/总字数(admin 数据源清单)。
func (p *Postgres) AllDatasources(ctx context.Context) []DatasourceKB {
if p.db == nil {
return nil
}
var out []DatasourceKB
p.db.WithContext(WithoutTenant(ctx)).Table("sundynix_kb k").
Select("k.id, k.name, k.kind, k.tenant_id, coalesce(t.name,'') as tenant_name, k.owner, "+
"count(d.id) as doc_count, coalesce(sum(d.size),0) as total_words").
Joins("left join sundynix_doc d on d.kb = k.name and d.space_id = k.space_id and d.deleted_at is null").
Joins("left join sundynix_tenant t on t.id = k.tenant_id").
Where("k.deleted_at is null").
Group("k.id, k.name, k.kind, k.tenant_id, t.name, k.owner").
Order("doc_count desc, k.created_at desc").
Scan(&out)
return out
}