diff --git a/backend/domain/ast.go b/backend/domain/ast.go new file mode 100644 index 0000000..1050de2 --- /dev/null +++ b/backend/domain/ast.go @@ -0,0 +1,12 @@ +package domain +type ParseResult struct { + FilePath string `json:"file_path"` + Definition string `json:"definition"` + Error string `json:"error,omitempty"` + Success bool `json:"success"` +} +type SaveAstReq struct { + UserID string `json:"user_id" validate:"required"` + ProjectID string `json:"project_id" validate:"required"` + Files []string `json:"files" validate:"required"` +} \ No newline at end of file diff --git a/backend/domain/workspace.go b/backend/domain/workspace.go index 58da027..a96f19d 100644 --- a/backend/domain/workspace.go +++ b/backend/domain/workspace.go @@ -14,6 +14,7 @@ type WorkspaceFileUsecase interface { Create(ctx context.Context, req *CreateWorkspaceFileReq) (*WorkspaceFile, error) Update(ctx context.Context, req *UpdateWorkspaceFileReq) (*WorkspaceFile, error) Delete(ctx context.Context, id string) error + GetAndSave(ctx context.Context, req *SaveAstReq) error GetByID(ctx context.Context, id string) (*WorkspaceFile, error) GetByPath(ctx context.Context, userID, workspaceID, path string) (*WorkspaceFile, error) List(ctx context.Context, req *ListWorkspaceFileReq) (*ListWorkspaceFileResp, error) @@ -22,6 +23,7 @@ type WorkspaceFileUsecase interface { Sync(ctx context.Context, req *SyncWorkspaceFileReq) (*SyncWorkspaceFileResp, error) } + // WorkspaceFileRepo 定义 WorkspaceFile 数据访问接口 type WorkspaceFileRepo interface { Create(ctx context.Context, req *CreateWorkspaceFileReq) (*db.WorkspaceFile, error) diff --git a/backend/internal/workspace/handler/http/v1/workspace.go b/backend/internal/workspace/handler/http/v1/workspace.go index bda4066..4b14088 100644 --- a/backend/internal/workspace/handler/http/v1/workspace.go +++ b/backend/internal/workspace/handler/http/v1/workspace.go @@ -92,6 +92,21 @@ func (h *WorkspaceFileHandler) GetByID(c *web.Context, req struct { return c.Success(file) } +// GetAndSave +// @Tags WorkspaceFile +// @Summary 获取并保存工作区文件 +// @param ctx +// @param req +// @return error +func (h *WorkspaceFileHandler) GetAndSave(ctx *web.Context, req *domain.SaveAstReq) error { + err := h.usecase.GetAndSave(ctx.Request().Context(), req) + if err != nil { + h.logger.Error("failed to get and save workspace files", "error", err, "count", len(req.Files)) + return err + } + return ctx.Success(nil) +} + // Update 更新工作区文件 // // @Tags WorkspaceFile diff --git a/backend/internal/workspace/usecase/workspace.go b/backend/internal/workspace/usecase/workspace.go index 16b281b..3988fb7 100644 --- a/backend/internal/workspace/usecase/workspace.go +++ b/backend/internal/workspace/usecase/workspace.go @@ -4,6 +4,7 @@ import ( "context" "crypto/sha256" "encoding/hex" + "encoding/json" "fmt" "log/slog" "strings" @@ -11,6 +12,7 @@ import ( "github.com/chaitin/MonkeyCode/backend/config" "github.com/chaitin/MonkeyCode/backend/db" "github.com/chaitin/MonkeyCode/backend/domain" + "github.com/chaitin/MonkeyCode/backend/pkg/cli" "github.com/chaitin/MonkeyCode/backend/pkg/cvt" ) @@ -121,6 +123,31 @@ func (u *WorkspaceFileUsecase) GetByID(ctx context.Context, id string) (*domain. return cvt.From(file, &domain.WorkspaceFile{}), nil } +func (u *WorkspaceFileUsecase) GetAndSave(ctx context.Context, req *domain.SaveAstReq) (error) { + results, err := cli.RunParseCLI("parse", "", req.Files...) + if err != nil { + return err + } + for _, res := range results { + file, err := u.repo.GetByPath(ctx, req.UserID, req.ProjectID, res.FilePath) + if err != nil { + return err + } + + astData, err := json.Marshal(res.Definition) + if err != nil { + return err + } + _, err = u.repo.Update(ctx, file.ID.String(), func(up *db.WorkspaceFileUpdateOne) error { + return up.SetContent(string(astData)).Exec(ctx) + }) + if err != nil { + return err + } + } + return nil +} + func (u *WorkspaceFileUsecase) GetByPath(ctx context.Context, userID, workspaceID, path string) (*domain.WorkspaceFile, error) { file, err := u.repo.GetByPath(ctx, userID, workspaceID, path) if err != nil { diff --git a/backend/pkg/cli/cli.go b/backend/pkg/cli/cli.go new file mode 100644 index 0000000..f44a6d1 --- /dev/null +++ b/backend/pkg/cli/cli.go @@ -0,0 +1,33 @@ +package cli + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" + "strings" + + "github.com/chaitin/MonkeyCode/backend/domain" +) + +// RunParseCLI +// @Description ctcode-cli 支持通过 CLI 的方式获取文件的 AST 信息 +// @param command ["parse"] - 解析文件为 AST 树 +// @param flag ["-s", "--successOnly"] - 是否只返回成功的结果 +// @param paths 要解析的文件路径 +// @return []ParseResult 解析结果数组 +// @return error 错误信息 +func RunParseCLI(command string, flag string, paths ...string) ([]domain.ParseResult, error) { + cmd := exec.Command("ctcode-cli", command, flag, strings.Join(paths, " ")) + cmd.Env = os.Environ() + output, err := cmd.CombinedOutput() + fmt.Printf(`err: %s, output: %s\n`, fmt.Sprint(err), string(output)) + if err != nil { + return []domain.ParseResult{}, err + } + var results []domain.ParseResult + if err := json.Unmarshal(output, &results); err != nil { + panic(err) + } + return results, nil +}