package inbox import ( "errors" "gorm.io/gorm" "senlinai-agent/backend/internal/models" ) type CaptureInput struct { ProjectID uint UserID uint SourceType string Title string Body string } type Suggestion struct { Kind string `json:"kind"` Title string `json:"title"` Body string `json:"body"` } type Analyzer interface { Analyze(item models.SenlinAgentInboxItem, userID uint) ([]Suggestion, error) } type StaticAnalyzer struct { Suggestions []Suggestion } func (a StaticAnalyzer) Analyze(item models.SenlinAgentInboxItem, userID uint) ([]Suggestion, error) { return a.Suggestions, nil } type Service struct { analyzer Analyzer } func NewService(analyzer Analyzer) *Service { return &Service{analyzer: analyzer} } func (s *Service) Capture(input CaptureInput) (*models.SenlinAgentInboxItem, error) { if input.ProjectID == 0 || input.UserID == 0 { return nil, errors.New("project and user are required") } if input.SourceType == "" { return nil, errors.New("source type is required") } item := &models.SenlinAgentInboxItem{ ProjectID: input.ProjectID, CreatedBy: input.UserID, SourceType: input.SourceType, Title: input.Title, Body: input.Body, Status: "open", } return item, models.DBService.Create(item).Error } func (s *Service) Analyze(itemID uint, userID uint) ([]Suggestion, error) { var item models.SenlinAgentInboxItem if err := models.DBService.First(&item, itemID).Error; err != nil { return nil, err } if s.analyzer == nil { return []Suggestion{}, nil } return s.analyzer.Analyze(item, userID) } func (s *Service) Confirm(itemID uint, selected []Suggestion) error { return models.DBService.Transaction(func(tx *gorm.DB) error { var item models.SenlinAgentInboxItem if err := tx.First(&item, itemID).Error; err != nil { return err } sourceInboxItemID := item.ID for _, suggestion := range selected { switch suggestion.Kind { case "task": if err := tx.Create(&models.SenlinAgentTask{ProjectID: item.ProjectID, CreatedBy: item.CreatedBy, SourceInboxItemID: &sourceInboxItemID, Title: suggestion.Title, Description: suggestion.Body}).Error; err != nil { return err } case "note": if err := tx.Create(&models.SenlinAgentNote{ProjectID: item.ProjectID, CreatedBy: item.CreatedBy, SourceInboxItemID: &sourceInboxItemID, Title: suggestion.Title, Markdown: suggestion.Body}).Error; err != nil { return err } case "source": if err := tx.Create(&models.SenlinAgentSource{ProjectID: item.ProjectID, CreatedBy: item.CreatedBy, SourceInboxItemID: &sourceInboxItemID, Kind: "link", Title: suggestion.Title, ContentText: suggestion.Body}).Error; err != nil { return err } default: return errors.New("unsupported suggestion kind") } } return tx.Model(&item).Update("status", "processed").Error }) }