feat: Implement Qwen adapter and enhance AI platform detection

- Added a new Qwen adapter to facilitate monitoring through Playwright, leveraging internal text/chat managers.
- Updated account detection logic to recognize Qwen sessions based on persisted cookies, improving binding reliability.
- Relaxed authentication requirements for monitor tasks, allowing anonymous execution for certain AI platforms.
- Enhanced the generic AI platform detection to include Qwen-specific logic, ensuring accurate session identification.
- Modified the monitoring dashboard to include runtime state indicating if the current user's desktop client is online.
- Updated various files including `account-binder.ts`, `runtime-controller.ts`, and `monitoring_service.go` to support new features and improvements.
This commit is contained in:
2026-04-20 19:10:35 +08:00
parent 69fd182755
commit 7abac1e9c4
13 changed files with 973 additions and 105 deletions
@@ -40,6 +40,7 @@ func NewMonitoringService(businessPool, monitoringPool *pgxpool.Pool) *Monitorin
type MonitoringDashboardCompositeResponse struct {
Overview MonitoringOverview `json:"overview"`
Runtime MonitoringDashboardRuntime `json:"runtime"`
PlatformBreakdown []MonitoringPlatformDaily `json:"platform_breakdown"`
BrandTimeBuckets []MonitoringTimeBucket `json:"brand_time_buckets"`
HotQuestions []MonitoringHotQuestion `json:"hot_questions"`
@@ -47,6 +48,10 @@ type MonitoringDashboardCompositeResponse struct {
CitedArticles []MonitoringCitedArticle `json:"cited_articles"`
}
type MonitoringDashboardRuntime struct {
CurrentUserClientOnline bool `json:"current_user_client_online"`
}
type MonitoringOverview struct {
CollectionMode string `json:"collection_mode"`
MentionRate *float64 `json:"mention_rate"`
@@ -285,6 +290,11 @@ func (s *MonitoringService) DashboardComposite(
return nil, err
}
runtime, err := s.loadDashboardRuntime(ctx, actor)
if err != nil {
return nil, err
}
configuredQuestions, err := s.loadConfiguredQuestions(ctx, actor.TenantID, brand.ID, keywordID)
if err != nil {
return nil, err
@@ -338,6 +348,7 @@ func (s *MonitoringService) DashboardComposite(
return &MonitoringDashboardCompositeResponse{
Overview: overview,
Runtime: runtime,
PlatformBreakdown: platformBreakdown,
BrandTimeBuckets: timeBuckets,
HotQuestions: hotQuestions,
@@ -346,6 +357,17 @@ func (s *MonitoringService) DashboardComposite(
}, nil
}
func (s *MonitoringService) loadDashboardRuntime(ctx context.Context, actor auth.Actor) (MonitoringDashboardRuntime, error) {
online, err := s.hasOnlineClientForUser(ctx, actor.TenantID, actor.PrimaryWorkspaceID, actor.UserID)
if err != nil {
return MonitoringDashboardRuntime{}, err
}
return MonitoringDashboardRuntime{
CurrentUserClientOnline: online,
}, nil
}
func (s *MonitoringService) QuestionDetail(ctx context.Context, brandID, questionID int64, dateFrom, dateTo string, questionHash string, aiPlatformID *string) (*MonitoringQuestionDetailResponse, error) {
actor := auth.MustActor(ctx)
@@ -502,12 +524,12 @@ func (s *MonitoringService) CollectNow(ctx context.Context, brandID int64, keywo
platforms := resolveMonitoringPlatforms(quota.EnabledPlatforms)
questionIDs := configuredQuestionIDs(configuredQuestions)
clientID, err := s.resolveCollectNowClientID(ctx, actor.TenantID, actor.PrimaryWorkspaceID, quota.PrimaryClientID)
clientID, err := s.resolveCollectNowClientID(ctx, actor.TenantID, actor.PrimaryWorkspaceID, actor.UserID)
if err != nil {
return nil, err
}
if clientID == nil {
return nil, response.ErrConflict(40941, "desktop_client_offline", "primary monitoring desktop client is offline")
return nil, response.ErrConflict(40941, "desktop_client_offline", "current user's monitoring desktop client is offline")
}
if err := s.ensureClientOnline(ctx, actor.TenantID, actor.PrimaryWorkspaceID, *clientID); err != nil {
@@ -589,47 +611,32 @@ func (s *MonitoringService) CollectNow(ctx context.Context, brandID int64, keywo
}, nil
}
func (s *MonitoringService) resolveCollectNowClientID(ctx context.Context, tenantID, workspaceID int64, preferred *uuid.UUID) (*uuid.UUID, error) {
if preferred != nil {
online, err := s.isClientOnline(ctx, tenantID, workspaceID, *preferred)
if err != nil {
return nil, err
}
if online {
return preferred, nil
}
}
fallbackID, err := s.findLatestOnlineClient(ctx, tenantID, workspaceID)
if err != nil {
return nil, err
}
if fallbackID == nil {
return nil, nil
}
if preferred == nil || *preferred != *fallbackID {
if err := s.persistPrimaryClientID(ctx, tenantID, workspaceID, *fallbackID); err != nil {
return nil, err
}
}
return fallbackID, nil
func (s *MonitoringService) resolveCollectNowClientID(ctx context.Context, tenantID, workspaceID, userID int64) (*uuid.UUID, error) {
return s.findLatestOnlineClientForUser(ctx, tenantID, workspaceID, userID)
}
func (s *MonitoringService) findLatestOnlineClient(ctx context.Context, tenantID, workspaceID int64) (*uuid.UUID, error) {
func (s *MonitoringService) hasOnlineClientForUser(ctx context.Context, tenantID, workspaceID, userID int64) (bool, error) {
clientID, err := s.findLatestOnlineClientForUser(ctx, tenantID, workspaceID, userID)
if err != nil {
return false, err
}
return clientID != nil, nil
}
func (s *MonitoringService) findLatestOnlineClientForUser(ctx context.Context, tenantID, workspaceID, userID int64) (*uuid.UUID, error) {
var clientID uuid.UUID
err := s.businessPool.QueryRow(ctx, `
SELECT id
FROM desktop_clients
WHERE tenant_id = $1
AND workspace_id = $2
AND user_id = $3
AND revoked_at IS NULL
AND last_seen_at IS NOT NULL
AND last_seen_at >= NOW() - $3::interval
AND last_seen_at >= NOW() - $4::interval
ORDER BY last_seen_at DESC, created_at DESC, id DESC
LIMIT 1
`, tenantID, workspaceID, formatPgInterval(monitoringOnlineDuration)).Scan(&clientID)
`, tenantID, workspaceID, userID, formatPgInterval(desktopClientPresenceTTL)).Scan(&clientID)
if err != nil {
if err == pgx.ErrNoRows {
return nil, nil
@@ -2463,7 +2470,7 @@ func (s *MonitoringService) ensureClientOnline(ctx context.Context, tenantID, wo
return err
}
if !online {
return response.ErrConflict(40941, "desktop_client_offline", "primary monitoring desktop client is offline")
return response.ErrConflict(40941, "desktop_client_offline", "current user's monitoring desktop client is offline")
}
return nil
}
@@ -2485,7 +2492,7 @@ func (s *MonitoringService) isClientOnline(ctx context.Context, tenantID, worksp
return false, response.ErrInternal(50041, "query_failed", "failed to inspect monitoring desktop client")
}
if !lastSeenAt.Valid || time.Since(lastSeenAt.Time) > monitoringOnlineDuration {
if !lastSeenAt.Valid || time.Since(lastSeenAt.Time) > desktopClientPresenceTTL {
return false, nil
}
return true, nil