feat(gateway): 多租户2b-A —— Task 加 owner/tenant 隔离 + 系统旁路修 admin 全局口径
- store.WithoutTenant(ctx):显式跨租户旁路,插件即使 ctx 带租户也不过滤。 修复增量2 引入的回归:admin SystemCounts 数 KB 时被 admin 自己租户误过滤。 AdminOverview 改用旁路 ctx → 任务/KB/Doc/Eval 恢复全平台口径。 - Task 加 TenantID(插件自动填) + Owner(提交者 user.id) + isTenantScoped()。 SaveTask 记录 owner;RecentTasks/RecentRuns 按 owner+租户过滤"我的运行"。 RecentRuns 是 raw Table 查询绕过插件,手动补 owner+tenant WHERE。 live 验证:A 提交任务 → 行 owner/tenant 自动填对 ✓;A 的 /runs 只见己方、 B 空、legacy 无 owner 行被排除 ✓;admin/overview tasks_total=39/kb_count=21 = DB 全局真值(旁路生效,回归已修)✓。 Eval/Doc/DocLink 的 tenant 传播见 2b-B。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -117,11 +117,12 @@ func migrateDocLinkToID(db *gorm.DB) {
|
||||
func (p *Postgres) Enabled() bool { return p.db != nil }
|
||||
|
||||
// SaveTask 持久化一次任务提交(best-effort:降级模式下静默跳过)。
|
||||
func (p *Postgres) SaveTask(ctx context.Context, id, graph string) error {
|
||||
func (p *Postgres) SaveTask(ctx context.Context, owner, id, graph string) error {
|
||||
if p.db == nil {
|
||||
return nil
|
||||
}
|
||||
return p.db.WithContext(ctx).Create(&Task{TaskID: id, Graph: graph, Status: contract.TaskSubmitted}).Error
|
||||
// TenantID 由 tenant 插件按请求 ctx 自动填;Owner 显式记录提交者(供个人工作台过滤)。
|
||||
return p.db.WithContext(ctx).Create(&Task{Owner: owner, TaskID: id, Graph: graph, Status: contract.TaskSubmitted}).Error
|
||||
}
|
||||
|
||||
// UpdateTaskStatus 流转任务状态(running/done/failed/timeout),由 dispatcher 经 NATS 回写驱动。
|
||||
@@ -270,12 +271,14 @@ func (p *Postgres) StatsOverview(ctx context.Context, owner string) *Overview {
|
||||
}
|
||||
|
||||
// RecentTasks 返回最近 n 条任务(工作台「近期运行」feed)。
|
||||
func (p *Postgres) RecentTasks(ctx context.Context, n int) []Task {
|
||||
// RecentTasks 返回某用户最近 n 条任务(个人工作台「近期运行」feed)。
|
||||
// owner 过滤"我的运行";tenant 由插件自动叠加(双保险:跨用户/跨租户都隔离)。
|
||||
func (p *Postgres) RecentTasks(ctx context.Context, owner string, n int) []Task {
|
||||
if p.db == nil {
|
||||
return nil
|
||||
}
|
||||
var out []Task
|
||||
p.db.WithContext(ctx).Order("created_at desc").Limit(n).Find(&out)
|
||||
p.db.WithContext(ctx).Where("owner = ?", owner).Order("created_at desc").Limit(n).Find(&out)
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -289,18 +292,22 @@ type RunRow struct {
|
||||
EvalOverall float64 `json:"eval_overall"`
|
||||
}
|
||||
|
||||
// RecentRuns 返回最近 n 条运行(含评测分级,供「运行历史」列表)。
|
||||
func (p *Postgres) RecentRuns(ctx context.Context, n int) []RunRow {
|
||||
// RecentRuns 返回某用户最近 n 条运行(含评测分级,供「运行历史」列表)。
|
||||
// 注:raw Table 查询绕过 gorm 模型回调 → 租户插件不生效,故此处**手动**按 owner(+ctx 租户) 过滤。
|
||||
func (p *Postgres) RecentRuns(ctx context.Context, owner string, n int) []RunRow {
|
||||
if p.db == nil {
|
||||
return nil
|
||||
}
|
||||
var out []RunRow
|
||||
p.db.WithContext(ctx).Table("sundynix_task as t").
|
||||
q := p.db.WithContext(ctx).Table("sundynix_task as t").
|
||||
Select("t.task_id, t.status, t.detail, t.created_at as at, " +
|
||||
"coalesce(e.level,'') as eval_level, coalesce(e.overall,0) as eval_overall").
|
||||
Joins("left join sundynix_eval e on e.task_id = t.task_id").
|
||||
Where("t.deleted_at is null").
|
||||
Order("t.created_at desc").Limit(n).Scan(&out)
|
||||
Where("t.deleted_at is null AND t.owner = ?", owner)
|
||||
if tid := tenantFromCtx(ctx); tid != "" && !isSystemCtx(ctx) {
|
||||
q = q.Where("t.tenant_id = ?", tid)
|
||||
}
|
||||
q.Order("t.created_at desc").Limit(n).Scan(&out)
|
||||
return out
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user