feat(monitoring): generate daily tasks and optimize heartbeat writes
Adds a MonitoringDailyTaskWorker that materializes and dispatches daily collect tasks from active subscription/desktop-client state, with per-run atomic metrics and a Prometheus collector. Dashboard and question-detail APIs now surface a platform_authorization_status so the UI can distinguish no-desktop-client, no-authorized-platforms, and authorized states; quota/access-state lookups resolve by request workspace instead of tenant. Hardens heartbeat: a desktop_client_primary_leases table gives per-(tenant, workspace) sticky primary selection, read-mostly lease resolution avoids a per-heartbeat transaction, and unchanged platform access reports skip snapshot upserts outside a 10-minute refresh window. Adds heartbeat primary/snapshot metrics exposed via token-protected tenant-api /api/internal/metrics endpoints plus Prometheus. Monitoring quota seed is removed from dev-seed since daily plans now derive from desktop bindings, and the projection uses the canonical six-platform catalog so platforms with zero samples still render. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -246,10 +246,6 @@ func seedBusinessData(ctx context.Context, pool *pgxpool.Pool, membershipCfg con
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := ensureMonitoringQuota(ctx, tx, state); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := tx.Commit(ctx); err != nil {
|
||||
return nil, fmt.Errorf("commit business seed: %w", err)
|
||||
}
|
||||
@@ -692,28 +688,6 @@ func ensureDesktopClient(ctx context.Context, tx pgx.Tx, tenantID, workspaceID,
|
||||
return ensuredID, nil
|
||||
}
|
||||
|
||||
func ensureMonitoringQuota(ctx context.Context, tx pgx.Tx, state *seedState) error {
|
||||
enabledPlatforms, _ := json.Marshal([]string{"yuanbao", "kimi", "wenxin", "deepseek", "doubao", "qwen"})
|
||||
_, err := tx.Exec(ctx, `
|
||||
INSERT INTO tenant_monitoring_quotas (
|
||||
tenant_id, workspace_id, max_brands, collection_mode, question_selection_mode, collect_frequency,
|
||||
enabled_platforms, primary_client_id, task_daily_hard_cap, plan_tier
|
||||
)
|
||||
VALUES ($1, $2, 1, 'desktop', 'best_effort_all', 'daily', $3::jsonb, $4, 36, 'free')
|
||||
ON CONFLICT (tenant_id) DO UPDATE
|
||||
SET workspace_id = EXCLUDED.workspace_id,
|
||||
collection_mode = EXCLUDED.collection_mode,
|
||||
question_selection_mode = EXCLUDED.question_selection_mode,
|
||||
collect_frequency = EXCLUDED.collect_frequency,
|
||||
enabled_platforms = EXCLUDED.enabled_platforms,
|
||||
primary_client_id = EXCLUDED.primary_client_id,
|
||||
task_daily_hard_cap = EXCLUDED.task_daily_hard_cap,
|
||||
plan_tier = EXCLUDED.plan_tier,
|
||||
updated_at = NOW()
|
||||
`, state.TenantID, state.WorkspaceID, string(enabledPlatforms), state.DesktopClientID)
|
||||
return wrapSeedErr("upsert monitoring quota", err)
|
||||
}
|
||||
|
||||
func clearMonitoringData(ctx context.Context, tx pgx.Tx, state *seedState) error {
|
||||
pairs := []string{
|
||||
`DELETE FROM monitoring_citation_facts WHERE tenant_id = $1 AND brand_id = $2`,
|
||||
|
||||
@@ -32,7 +32,7 @@ func main() {
|
||||
app.DesktopTaskStreams.Run(workerCtx)
|
||||
app.DesktopDispatch.Run(workerCtx)
|
||||
|
||||
monitoringService := tenantapp.NewMonitoringService(app.DB, app.MonitoringDB, app.RabbitMQ, app.Config.MonitoringDispatch, app.Logger)
|
||||
monitoringService := tenantapp.NewMonitoringService(app.DB, app.MonitoringDB, app.RabbitMQ, app.Config.MonitoringDispatch, app.Config.BrandLibrary, app.Logger)
|
||||
monitoringCallbackService := tenantapp.NewMonitoringCallbackService(app.DB, app.MonitoringDB, app.RabbitMQ, app.LLM, app.Logger)
|
||||
tenantapp.NewMonitoringCollectOutboxWorker(monitoringService, app.Logger).Start(workerCtx)
|
||||
tenantapp.NewMonitoringResultIngestWorker(monitoringCallbackService, app.RabbitMQ, app.Logger, app.Config.MonitoringWorkers).Start(workerCtx)
|
||||
|
||||
Reference in New Issue
Block a user