package platforms import ( "code-review/services/types" "code-review/utils" "fmt" "log" "strings" ) type GiteaEvent struct { apiBase string auth *AuthConfig Event string client *httpClient Secret string `json:"secret"` Ref string `json:"ref"` Before string `json:"before"` After string `json:"after"` CompareURL string `json:"compare_url"` Commits []struct { ID string `json:"sha"` Message string `json:"commit.message"` URL string `json:"url"` HTMLURL string `json:"html_url"` Created string `json:"created"` Author struct { Active bool `json:"active"` AvatarURL string `json:"avatar_url"` Created string `json:"created"` Description string `json:"description"` Email string `json:"email"` FollowersCount int `json:"followers_count"` FollowingCount int `json:"following_count"` FullName string `json:"full_name"` HTMLURL string `json:"html_url"` ID int `json:"id"` IsAdmin bool `json:"is_admin"` Language string `json:"language"` LastLogin string `json:"last_login"` Location string `json:"location"` Login string `json:"login"` LoginName string `json:"login_name"` ProhibitLogin bool `json:"prohibit_login"` Restricted bool `json:"restricted"` SourceID int `json:"source_id"` Visibility string `json:"visibility"` Website string `json:"website"` } `json:"author"` Committer struct { Active bool `json:"active"` AvatarURL string `json:"avatar_url"` Created string `json:"created"` Description string `json:"description"` Email string `json:"email"` FollowersCount int `json:"followers_count"` FollowingCount int `json:"following_count"` FullName string `json:"full_name"` HTMLURL string `json:"html_url"` ID int `json:"id"` IsAdmin bool `json:"is_admin"` Language string `json:"language"` LastLogin string `json:"last_login"` Location string `json:"location"` Login string `json:"login"` LoginName string `json:"login_name"` ProhibitLogin bool `json:"prohibit_login"` Restricted bool `json:"restricted"` SourceID int `json:"source_id"` Visibility string `json:"visibility"` Website string `json:"website"` } `json:"committer"` Commit struct { Author struct { Date string `json:"date"` Email string `json:"email"` Name string `json:"name"` } `json:"author"` Committer struct { Date string `json:"date"` Email string `json:"email"` Name string `json:"name"` } `json:"committer"` Message string `json:"message"` Tree struct { Created string `json:"created"` SHA string `json:"sha"` URL string `json:"url"` } `json:"tree"` URL string `json:"url"` Verification struct { Payload string `json:"payload"` Reason string `json:"reason"` Signature string `json:"signature"` Signer struct { Email string `json:"email"` Name string `json:"name"` Username string `json:"username"` } `json:"signer"` Verified bool `json:"verified"` } `json:"verification"` } `json:"commit"` Files []struct { Filename string `json:"filename"` Status string `json:"status"` } `json:"files"` Parents []struct { Created string `json:"created"` SHA string `json:"sha"` URL string `json:"url"` } `json:"parents"` Stats struct { Additions int `json:"additions"` Deletions int `json:"deletions"` Total int `json:"total"` } `json:"stats"` } `json:"commits"` Repository struct { ID int `json:"id"` Name string `json:"name"` FullName string `json:"full_name"` Description string `json:"description"` Private bool `json:"private"` Fork bool `json:"fork"` HTMLURL string `json:"html_url"` SSHURL string `json:"ssh_url"` CloneURL string `json:"clone_url"` Website string `json:"website"` StarsCount int `json:"stars_count"` ForksCount int `json:"forks_count"` WatchersCount int `json:"watchers_count"` OpenIssuesCount int `json:"open_issues_count"` DefaultBranch string `json:"default_branch"` CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` Owner struct { ID int `json:"id"` Login string `json:"login"` FullName string `json:"full_name"` Email string `json:"email"` AvatarURL string `json:"avatar_url"` Username string `json:"username"` } `json:"owner"` } `json:"repository"` Pusher struct { ID int `json:"id"` Login string `json:"login"` FullName string `json:"full_name"` Email string `json:"email"` AvatarURL string `json:"avatar_url"` Username string `json:"username"` } `json:"pusher"` Sender struct { ID int `json:"id"` Login string `json:"login"` FullName string `json:"full_name"` Email string `json:"email"` AvatarURL string `json:"avatar_url"` Username string `json:"username"` } `json:"sender"` } // NewGiteaEvent 创建 Gitea 事件 func NewGiteaEvent(apiBase string, auth *AuthConfig, event string) *GiteaEvent { return &GiteaEvent{ apiBase: apiBase, auth: auth, Event: event, } } // 定义 Gitea commit 响应的结构 type giteaCommitResponse struct { Commit struct { Message string `json:"message"` Author struct { Date string `json:"date"` Email string `json:"email"` Name string `json:"name"` } `json:"author"` Committer struct { Date string `json:"date"` Email string `json:"email"` Name string `json:"name"` } `json:"committer"` } `json:"commit"` Files []struct { Filename string `json:"filename"` Status string `json:"status"` Additions int `json:"additions"` Deletions int `json:"deletions"` Changes int `json:"changes"` Content string `json:"content"` } `json:"files"` } func (e *GiteaEvent) ExtractChanges() (*types.CodeChanges, error) { // 检查是否有提交记录 if len(e.Commits) == 0 { log.Printf("没有提交记录,跳过代码审查") return nil, nil } // 检查是否跳过代码审查 for _, commit := range e.Commits { if strings.Contains(commit.Commit.Message, "[skip codereview]") { log.Printf("跳过代码审查: commit=%s", commit.ID) return nil, nil } } if e.client == nil { e.client = newHTTPClient(e.apiBase, e.auth) log.Printf("初始化 HTTP 客户端: url=%s", e.apiBase) } changes := &types.CodeChanges{ Repository: e.Repository.FullName, Branch: e.Ref, CommitID: e.After, Files: make([]types.FileChange, 0), } for _, commit := range e.Commits { // 直接获取 diff 内容 apiPath := fmt.Sprintf("/api/v1/repos/%s/%s/git/commits/%s", e.Repository.Owner.Login, e.Repository.Name, e.After) var diffContent giteaCommitResponse if err := e.client.get(apiPath, &diffContent); err != nil { log.Printf("获取提交详情失败: commit=%s, error=%v", commit.ID, err) continue } // 处理每个文件的变更 for _, file := range diffContent.Files { var content strings.Builder content.WriteString(fmt.Sprintf("### 变更说明\n")) content.WriteString(fmt.Sprintf("提交信息: %s\n\n", diffContent.Commit.Message)) status := "modified" switch file.Status { case "added": status = "added" content.WriteString(fmt.Sprintf("新增文件: %s\n\n", file.Filename)) case "removed": status = "deleted" content.WriteString(fmt.Sprintf("删除文件: %s\n\n", file.Filename)) case "renamed": status = "renamed" content.WriteString(fmt.Sprintf("重命名文件: %s\n\n", file.Filename)) default: content.WriteString(fmt.Sprintf("修改文件: %s\n\n", file.Filename)) } if file.Content != "" { content.WriteString("### 变更内容\n") content.WriteString("```diff\n") content.WriteString(file.Content) content.WriteString("\n```\n") } changes.Files = append(changes.Files, types.FileChange{ Path: file.Filename, Content: content.String(), Type: utils.ParseFileType(status), }) } } return changes, nil } func (e *GiteaEvent) PostComments(result *types.ReviewResult) error { if e.client == nil { e.client = newHTTPClient(e.apiBase, e.auth) } // 创建 issue path := fmt.Sprintf("/api/v1/repos/%s/%s/issues", e.Repository.Owner.Login, e.Repository.Name) issueData := map[string]interface{}{ "title": fmt.Sprintf("AI 代码审查 - %s", e.After[:7]), "body": utils.FormatReviewResult(result), } if err := e.client.post(path, issueData); err != nil { log.Printf("创建 issue 失败: path=%s, error=%v", path, err) return fmt.Errorf("创建 issue 失败: %w", err) } log.Printf("成功创建 issue: path=%s, commitID=%s", path, e.After[:7]) return nil } func (e *GiteaEvent) GetPlatform() string { return "gitea" }