Files
tutti/services/tuttid/data/workspace/sqlite_issue_attachment_create.go
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

78 lines
2.8 KiB
Go

package workspace
import (
"context"
"database/sql"
"errors"
"fmt"
workspaceissues "github.com/tutti-os/tutti/packages/workspace/issues"
)
// CreateIssueWithContextRefs atomically commits an Issue, its managed-file
// references, and topic activity. Attachment bytes are staged before this
// transaction and startup reconciliation removes only files that never gained
// a committed reference.
func (s *SQLiteStore) CreateIssueWithContextRefs(
ctx context.Context,
issue workspaceissues.Issue,
refs []workspaceissues.ContextRef,
) (workspaceissues.Issue, []workspaceissues.ContextRef, error) {
if err := s.ensureIssueDatabase(); err != nil {
return workspaceissues.Issue{}, nil, err
}
if err := s.ensureIssueWorkspace(ctx, issue.WorkspaceID); err != nil {
return workspaceissues.Issue{}, nil, err
}
if len(refs) == 0 {
return workspaceissues.Issue{}, nil, workspaceissues.ErrInvalidArgument
}
tx, err := s.writeDB.BeginTx(ctx, nil)
if err != nil {
return workspaceissues.Issue{}, nil, fmt.Errorf("begin create workspace issue with context refs: %w", err)
}
defer func() { _ = tx.Rollback() }()
var topicExists int
err = tx.QueryRowContext(ctx, `
SELECT 1 FROM workspace_issue_topics WHERE workspace_id = ? AND topic_id = ?
`, issue.WorkspaceID, issue.TopicID).Scan(&topicExists)
if errors.Is(err, sql.ErrNoRows) {
return workspaceissues.Issue{}, nil, workspaceissues.ErrTopicNotFound
}
if err != nil {
return workspaceissues.Issue{}, nil, fmt.Errorf("get workspace issue topic for attachment create: %w", err)
}
createdIssue, err := insertWorkspaceIssue(ctx, tx, issue)
if err != nil {
return workspaceissues.Issue{}, nil, err
}
savedRefs := make([]workspaceissues.ContextRef, 0, len(refs))
for _, ref := range refs {
if ref.WorkspaceID != issue.WorkspaceID || ref.IssueID != issue.IssueID ||
ref.TaskID != "" || ref.ParentKind != workspaceissues.ContextRefParentIssue {
return workspaceissues.Issue{}, nil, workspaceissues.ErrInvalidArgument
}
created, err := insertWorkspaceIssueContextRef(ctx, tx, ref)
if err != nil {
return workspaceissues.Issue{}, nil, err
}
savedRefs = append(savedRefs, created)
}
result, err := tx.ExecContext(ctx, `
UPDATE workspace_issue_topics
SET last_activity_at_unix_ms = ?
WHERE workspace_id = ? AND topic_id = ?
`, issue.UpdatedAtUnixMS, issue.WorkspaceID, issue.TopicID)
if err != nil {
return workspaceissues.Issue{}, nil, fmt.Errorf("touch workspace issue topic activity during attachment create: %w", err)
}
if err := requireRowsAffected(result, workspaceissues.ErrTopicNotFound, "touch workspace issue topic activity during attachment create"); err != nil {
return workspaceissues.Issue{}, nil, err
}
if err := tx.Commit(); err != nil {
return workspaceissues.Issue{}, nil, fmt.Errorf("commit create workspace issue with context refs: %w", err)
}
return createdIssue, savedRefs, nil
}