package objectrefs import ( "encoding/json" "sort" "strings" "eai_agentplatform/backend/internal/dal" "eai_agentplatform/backend/internal/model" specialistmodel "eai_agentplatform/backend/internal/specialists/model" xappmodel "eai_agentplatform/backend/internal/xapps/model" ) type SpecialistRuntimeRefs struct { XAppKeys []string ProjectIDs []uint TaskCount int64 TaskRunCount int64 ArtifactCount int64 AICallCount int64 } type SkillRuntimeRefs struct { SpecialistKeys []string XAppKeys []string ProjectIDs []uint } type XAppRuntimeRefs struct { FavoriteUserIDs []uint RecentUserIDs []uint CustomUserIDs []uint } func ParseKeyList(raw string) []string { trimmed := strings.TrimSpace(raw) if trimmed == "" { return nil } var keys []string if err := json.Unmarshal([]byte(trimmed), &keys); err != nil { return nil } out := make([]string, 0, len(keys)) seen := make(map[string]bool, len(keys)) for _, key := range keys { key = strings.TrimSpace(key) if key == "" || seen[key] { continue } seen[key] = true out = append(out, key) } return out } func listProjectIDsByKeyField(field string, key string) []uint { key = strings.TrimSpace(key) if key == "" || dal.DB == nil { return nil } var projects []model.Project if err := dal.DB.Select("id, " + field).Find(&projects).Error; err != nil { return nil } ids := make([]uint, 0) for _, project := range projects { var raw string switch field { case "specialist_keys": raw = project.SpecialistKeys case "skill_keys": raw = project.SkillKeys default: continue } for _, item := range ParseKeyList(raw) { if item == key { ids = append(ids, project.ID) break } } } sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] }) return ids } func listXAppKeysByField(field string, key string) []string { key = strings.TrimSpace(key) if key == "" || dal.DB == nil { return nil } var items []xappmodel.XAppDefinition if err := dal.DB.Select("key, "+field).Where(field+" = ?", key).Find(&items).Error; err != nil { return nil } out := make([]string, 0, len(items)) for _, item := range items { if trimmed := strings.TrimSpace(item.Key); trimmed != "" { out = append(out, trimmed) } } sort.Strings(out) return out } func listSpecialistKeysBindingSkill(skillKey string) []string { skillKey = strings.TrimSpace(skillKey) if skillKey == "" || dal.DB == nil { return nil } var items []specialistmodel.Specialist if err := dal.DB.Select("key, allowed_skills").Find(&items).Error; err != nil { return nil } out := make([]string, 0) for _, item := range items { for _, key := range ParseKeyList(item.AllowedSkills) { if key == skillKey { out = append(out, strings.TrimSpace(item.Key)) break } } } sort.Strings(out) return out } func CollectSpecialistRuntimeRefs(key string) SpecialistRuntimeRefs { key = strings.TrimSpace(key) refs := SpecialistRuntimeRefs{ XAppKeys: listXAppKeysByField("specialist_key", key), ProjectIDs: listProjectIDsByKeyField("specialist_keys", key), } if key == "" || dal.DB == nil { return refs } dal.DB.Model(&model.TaskRecord{}).Where("specialist_key = ?", key).Count(&refs.TaskCount) dal.DB.Model(&model.TaskRun{}).Where("specialist_key = ?", key).Count(&refs.TaskRunCount) dal.DB.Model(&model.TaskArtifact{}).Where("specialist_key = ?", key).Count(&refs.ArtifactCount) dal.DB.Model(&model.AiCallLog{}).Where("specialist_key = ?", key).Count(&refs.AICallCount) return refs } func CollectSkillRuntimeRefs(key string) SkillRuntimeRefs { key = strings.TrimSpace(key) return SkillRuntimeRefs{ SpecialistKeys: listSpecialistKeysBindingSkill(key), XAppKeys: listXAppKeysByField("skill_key", key), ProjectIDs: listProjectIDsByKeyField("skill_keys", key), } } func listUserIDsByXAppCenterField(field string, key string) []uint { key = strings.TrimSpace(key) if key == "" || dal.DB == nil { return nil } var rows []xappmodel.UserXAppCenter if err := dal.DB.Select("user_id, favorite_keys, recent_keys, custom_xapps").Find(&rows).Error; err != nil { return nil } userIDs := make([]uint, 0) for _, row := range rows { var matched bool switch field { case "favorite_keys": for _, item := range ParseKeyList(row.FavoriteKeys) { if item == key { matched = true break } } case "recent_keys": for _, item := range ParseKeyList(row.RecentKeys) { if item == key { matched = true break } } case "custom_xapps": var items []struct { Key string `json:"key"` } if err := json.Unmarshal([]byte(strings.TrimSpace(row.CustomXApps)), &items); err == nil { for _, item := range items { if strings.TrimSpace(item.Key) == key { matched = true break } } } } if matched { userIDs = append(userIDs, row.UserID) } } sort.Slice(userIDs, func(i, j int) bool { return userIDs[i] < userIDs[j] }) return userIDs } func CollectXAppRuntimeRefs(key string) XAppRuntimeRefs { key = strings.TrimSpace(key) return XAppRuntimeRefs{ FavoriteUserIDs: listUserIDsByXAppCenterField("favorite_keys", key), RecentUserIDs: listUserIDsByXAppCenterField("recent_keys", key), CustomUserIDs: listUserIDsByXAppCenterField("custom_xapps", key), } }