Compare commits
22 Commits
8277c79d23
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bcc2cbdad | ||
|
|
7187205143 | ||
|
|
3b804b83da | ||
|
|
a714d4a3a9 | ||
|
|
1fac55115d | ||
|
|
3759295afa | ||
|
|
099bdfc7d9 | ||
|
|
3b902509f7 | ||
|
|
ec1fa93033 | ||
|
|
2b0ee0e61c | ||
|
|
906858dbd6 | ||
|
|
b4cc2347e5 | ||
|
|
1147ca4733 | ||
|
|
cd7c13e49d | ||
|
|
e5000bcc73 | ||
|
|
9ddea39084 | ||
|
|
e83332ea6a | ||
|
|
ab36ea0e5d | ||
|
|
c6b1a1664c | ||
|
|
f9a32a9ecb | ||
|
|
56ec95e43a | ||
|
|
3617c68a91 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -21,4 +21,6 @@
|
||||
# Go workspace file
|
||||
go.work
|
||||
|
||||
test/*
|
||||
tests/*
|
||||
atomctl
|
||||
AGENTS.md
|
||||
|
||||
19
.vscode/settings.json
vendored
19
.vscode/settings.json
vendored
@@ -1,3 +1,20 @@
|
||||
{
|
||||
"editor.fontSize": 10
|
||||
"editor.fontSize": 10,
|
||||
"dbcode.connections": [
|
||||
{
|
||||
"connectionId": "l6fEOogu2kQt5G3BashDt",
|
||||
"name": "10.1.1.2",
|
||||
"driver": "postgres",
|
||||
"connectionType": "host",
|
||||
"host": "10.1.1.2",
|
||||
"port": 5432,
|
||||
"ssl": false,
|
||||
"username": "postgres",
|
||||
"password": "",
|
||||
"savePassword": "secretStorage",
|
||||
"database": "quyun",
|
||||
"readOnly": false,
|
||||
"connectionTimeout": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
17
cmd/buf.go
17
cmd/buf.go
@@ -2,7 +2,9 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -15,10 +17,15 @@ func CommandBuf(root *cobra.Command) {
|
||||
RunE: commandBufE,
|
||||
}
|
||||
|
||||
cmd.Flags().String("dir", ".", "Directory to run buf from")
|
||||
cmd.Flags().Bool("dry-run", false, "Preview buf command without executing")
|
||||
|
||||
root.AddCommand(cmd)
|
||||
}
|
||||
|
||||
func commandBufE(cmd *cobra.Command, args []string) error {
|
||||
dir := cmd.Flag("dir").Value.String()
|
||||
dryRun, _ := cmd.Flags().GetBool("dry-run")
|
||||
if _, err := exec.LookPath("buf"); err != nil {
|
||||
log.Warn("buf 命令不存在,正在安装 buf...")
|
||||
log.Info("go install github.com/bufbuild/buf/cmd/buf@v1.48.0")
|
||||
@@ -33,9 +40,19 @@ func commandBufE(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// preflight: ensure buf.yaml exists
|
||||
if _, err := os.Stat(filepath.Join(dir, "buf.yaml")); err != nil {
|
||||
log.Warnf("未找到 %s,buf generate 可能失败", filepath.Join(dir, "buf.yaml"))
|
||||
}
|
||||
|
||||
log.Info("buf 命令已存在,正在运行 buf generate...")
|
||||
log.Info("PROTOBUF GUIDE: https://buf.build/docs/best-practices/style-guide/")
|
||||
if dryRun {
|
||||
log.Infof("[dry-run] (cd %s && buf generate)", dir)
|
||||
return nil
|
||||
}
|
||||
generateCmd := exec.Command("buf", "generate")
|
||||
generateCmd.Dir = dir
|
||||
if err := generateCmd.Run(); err != nil {
|
||||
return fmt.Errorf("运行 buf generate 失败: %v", err)
|
||||
}
|
||||
|
||||
22
cmd/fmt.go
22
cmd/fmt.go
@@ -16,6 +16,9 @@ func CommandFmt(root *cobra.Command) {
|
||||
RunE: commandFmtE,
|
||||
}
|
||||
|
||||
cmd.Flags().Bool("check", false, "Check formatting without writing changes")
|
||||
cmd.Flags().String("path", ".", "Path to format (default .)")
|
||||
|
||||
root.AddCommand(cmd)
|
||||
}
|
||||
|
||||
@@ -34,8 +37,25 @@ func commandFmtE(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
check, _ := cmd.Flags().GetBool("check")
|
||||
path, _ := cmd.Flags().GetString("path")
|
||||
|
||||
if check {
|
||||
log.Info("运行 gofumpt 检查模式...")
|
||||
out, err := exec.Command("gofumpt", "-l", "-extra", path).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("运行 gofumpt 失败: %v", err)
|
||||
}
|
||||
if len(out) > 0 {
|
||||
fmt.Fprintln(os.Stdout, string(out))
|
||||
return fmt.Errorf("发现未格式化文件,请运行: gofumpt -l -extra -w %s", path)
|
||||
}
|
||||
log.Info("代码格式良好")
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Info("运行 gofumpt...")
|
||||
gofumptCmd := exec.Command("gofumpt", "-l", "-extra", "-w", ".")
|
||||
gofumptCmd := exec.Command("gofumpt", "-l", "-extra", "-w", path)
|
||||
gofumptCmd.Stdout = os.Stdout
|
||||
gofumptCmd.Stderr = os.Stderr
|
||||
if err := gofumptCmd.Run(); err != nil {
|
||||
|
||||
@@ -32,6 +32,10 @@ func CommandGenModel(root *cobra.Command) {
|
||||
RunE: commandGenModelE,
|
||||
}
|
||||
|
||||
cmd.Flags().String("schema", "", "Override database schema")
|
||||
cmd.Flags().Bool("rename-schemas", true, "Rename generated database/<db> to database/schemas")
|
||||
cmd.Flags().String("schemas-out", "database/schemas", "Schemas output directory when renaming")
|
||||
|
||||
root.AddCommand(cmd)
|
||||
}
|
||||
|
||||
@@ -45,6 +49,11 @@ func commandGenModelE(cmd *cobra.Command, args []string) error {
|
||||
return errors.Wrap(err, "get db")
|
||||
}
|
||||
|
||||
// optional schema override
|
||||
if s := cmd.Flag("schema").Value.String(); s != "" {
|
||||
dbConf.Schema = s
|
||||
}
|
||||
|
||||
v := viper.New()
|
||||
v.SetConfigType("yaml")
|
||||
v.SetConfigFile("database/transform.yaml")
|
||||
@@ -166,14 +175,16 @@ func commandGenModelE(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.RemoveAll("database/schemas"); err != nil {
|
||||
if rename, _ := cmd.Flags().GetBool("rename-schemas"); rename {
|
||||
out := cmd.Flag("schemas-out").Value.String()
|
||||
if err := os.RemoveAll(out); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dataPath := fmt.Sprintf("database/%s", cfg.Database)
|
||||
if err := os.Rename(dataPath, "database/schemas"); err != nil {
|
||||
if err := os.Rename(dataPath, out); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := astModel.Generate(generatedTables, transformer); err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -21,6 +22,8 @@ func CommandGenRoute(root *cobra.Command) {
|
||||
PostRunE: commandGenProviderE,
|
||||
}
|
||||
|
||||
cmd.Flags().String("path", ".", "Base path to scan (defaults to CWD)")
|
||||
|
||||
root.AddCommand(cmd)
|
||||
}
|
||||
|
||||
@@ -37,6 +40,11 @@ func commandGenRouteE(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// allow overriding via --path flag
|
||||
if f := cmd.Flag("path"); f != nil && f.Value.String() != "." && f.Value.String() != "" {
|
||||
path = f.Value.String()
|
||||
}
|
||||
|
||||
path, _ = filepath.Abs(path)
|
||||
|
||||
err = gomod.Parse(filepath.Join(path, "go.mod"))
|
||||
@@ -48,7 +56,7 @@ func commandGenRouteE(cmd *cobra.Command, args []string) error {
|
||||
|
||||
modulePath := filepath.Join(path, "app/http")
|
||||
if _, err := os.Stat(modulePath); os.IsNotExist(err) {
|
||||
log.Fatal("modules dir not exist, ", modulePath)
|
||||
return fmt.Errorf("routes directory not found: %s (set --path to your project root)", modulePath)
|
||||
}
|
||||
|
||||
// controllerPattern := regexp.MustCompile(`controller(_?\w+)?\.go`)
|
||||
|
||||
@@ -19,6 +19,8 @@ func CommandMigrate(root *cobra.Command) {
|
||||
RunE: commandMigrate,
|
||||
}
|
||||
cmd.Flags().StringP("config", "c", "config.toml", "database config file")
|
||||
cmd.Flags().String("dir", "database/migrations", "migrations directory")
|
||||
cmd.Flags().String("table", "migrations", "migrations table name")
|
||||
|
||||
root.AddCommand(cmd)
|
||||
}
|
||||
@@ -42,7 +44,10 @@ func commandMigrate(cmd *cobra.Command, args []string) error {
|
||||
action, args := args[0], args[1:]
|
||||
log.Infof("migration action: %s args: %+v", action, args)
|
||||
|
||||
goose.SetTableName("migrations")
|
||||
dir := cmd.Flag("dir").Value.String()
|
||||
table := cmd.Flag("table").Value.String()
|
||||
|
||||
return goose.RunContext(context.Background(), action, db, "database/migrations", args...)
|
||||
goose.SetTableName(table)
|
||||
|
||||
return goose.RunContext(context.Background(), action, db, dir, args...)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@ func CommandInit(root *cobra.Command) {
|
||||
Short: "new project/module",
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().BoolP("force", "f", false, "Force init project if exists")
|
||||
cmd.PersistentFlags().BoolP("force", "f", false, "Force overwrite existing files or directories")
|
||||
cmd.PersistentFlags().Bool("dry-run", false, "Preview actions without writing files")
|
||||
cmd.PersistentFlags().String("dir", ".", "Base directory for outputs")
|
||||
|
||||
cmds := []func(*cobra.Command){
|
||||
CommandNewProject,
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"go.ipao.vip/atomctl/pkg/utils/gomod"
|
||||
@@ -23,6 +24,8 @@ func CommandNewEvent(root *cobra.Command) {
|
||||
RunE: commandNewEventE,
|
||||
}
|
||||
|
||||
cmd.Flags().String("only", "", "仅生成: publisher 或 subscriber")
|
||||
|
||||
root.AddCommand(cmd)
|
||||
}
|
||||
|
||||
@@ -30,8 +33,13 @@ func commandNewEventE(cmd *cobra.Command, args []string) error {
|
||||
snakeName := lo.SnakeCase(args[0])
|
||||
camelName := lo.PascalCase(args[0])
|
||||
|
||||
publisherPath := "app/events/publishers"
|
||||
subscriberPath := "app/events/subscribers"
|
||||
// shared flags
|
||||
dryRun, _ := cmd.Flags().GetBool("dry-run")
|
||||
baseDir, _ := cmd.Flags().GetString("dir")
|
||||
only, _ := cmd.Flags().GetString("only")
|
||||
|
||||
publisherPath := filepath.Join(baseDir, "app/events/publishers")
|
||||
subscriberPath := filepath.Join(baseDir, "app/events/subscribers")
|
||||
|
||||
path, err := os.Getwd()
|
||||
if err != nil {
|
||||
@@ -44,13 +52,17 @@ func commandNewEventE(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if dryRun {
|
||||
fmt.Printf("[dry-run] mkdir -p %s\n", publisherPath)
|
||||
fmt.Printf("[dry-run] mkdir -p %s\n", subscriberPath)
|
||||
} else {
|
||||
if err := os.MkdirAll(publisherPath, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(subscriberPath, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = fs.WalkDir(templates.Events, "events", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
@@ -67,16 +79,23 @@ func commandNewEventE(cmd *cobra.Command, args []string) error {
|
||||
|
||||
var destPath string
|
||||
if relPath == "publisher.go.tpl" {
|
||||
if only == "subscriber" { return nil }
|
||||
destPath = filepath.Join(publisherPath, snakeName+".go")
|
||||
} else if relPath == "subscriber.go.tpl" {
|
||||
if only == "publisher" { return nil }
|
||||
destPath = filepath.Join(subscriberPath, snakeName+".go")
|
||||
}
|
||||
} else { return nil }
|
||||
|
||||
tmpl, err := template.ParseFS(templates.Events, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if dryRun {
|
||||
fmt.Printf("[dry-run] render > %s\n", destPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
destFile, err := os.Create(destPath)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -89,17 +108,27 @@ func commandNewEventE(cmd *cobra.Command, args []string) error {
|
||||
})
|
||||
})
|
||||
|
||||
topicStr := fmt.Sprintf("const Topic%s = %q\n", camelName, snakeName)
|
||||
// 写入到 app/events/topic.go
|
||||
topicFile, err := os.OpenFile("app/events/topics.go", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer topicFile.Close()
|
||||
// 写入或追加 topic 常量,避免重复。
|
||||
topicsPath := filepath.Join(baseDir, "app/events/topics.go")
|
||||
topicLine := fmt.Sprintf("const Topic%s = %q\n", camelName, snakeName)
|
||||
|
||||
_, err = topicFile.WriteString(topicStr)
|
||||
if err != nil {
|
||||
return err
|
||||
if dryRun {
|
||||
fmt.Printf("[dry-run] ensure topics file and add constant > %s\n", topicsPath)
|
||||
} else {
|
||||
// ensure file exists with basic header
|
||||
if _, statErr := os.Stat(topicsPath); os.IsNotExist(statErr) {
|
||||
if err := os.MkdirAll(filepath.Dir(topicsPath), os.ModePerm); err != nil { return err }
|
||||
header := "package events\n\n// topics generated by atomctl\n\n"
|
||||
if err := os.WriteFile(topicsPath, []byte(header), 0o644); err != nil { return err }
|
||||
}
|
||||
// check duplicate
|
||||
content, _ := os.ReadFile(topicsPath)
|
||||
if !strings.Contains(string(content), "Topic"+camelName+" ") && !strings.Contains(string(content), topicLine) {
|
||||
f, err := os.OpenFile(topicsPath, os.O_APPEND|os.O_WRONLY, 0o644)
|
||||
if err != nil { return err }
|
||||
defer f.Close()
|
||||
if _, err := f.WriteString(topicLine); err != nil { return err }
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("event 已创建: %s\n", snakeName)
|
||||
|
||||
@@ -29,7 +29,11 @@ func commandNewJobE(cmd *cobra.Command, args []string) error {
|
||||
snakeName := lo.SnakeCase(args[0])
|
||||
camelName := lo.PascalCase(args[0])
|
||||
|
||||
destPath := "app/jobs"
|
||||
// shared flags
|
||||
dryRun, _ := cmd.Flags().GetBool("dry-run")
|
||||
baseDir, _ := cmd.Flags().GetString("dir")
|
||||
|
||||
basePath := filepath.Join(baseDir, "app/jobs")
|
||||
|
||||
path, err := os.Getwd()
|
||||
if err != nil {
|
||||
@@ -42,9 +46,13 @@ func commandNewJobE(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(destPath, os.ModePerm); err != nil {
|
||||
if dryRun {
|
||||
fmt.Printf("[dry-run] mkdir -p %s\n", basePath)
|
||||
} else {
|
||||
if err := os.MkdirAll(basePath, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = fs.WalkDir(templates.Jobs, "jobs", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
@@ -55,13 +63,18 @@ func commandNewJobE(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
destPath := filepath.Join(destPath, snakeName+".go")
|
||||
filePath := filepath.Join(basePath, snakeName+".go")
|
||||
tmpl, err := template.ParseFS(templates.Jobs, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
destFile, err := os.Create(destPath)
|
||||
if dryRun {
|
||||
fmt.Printf("[dry-run] render > %s\n", filePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
destFile, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
@@ -9,10 +10,11 @@ import (
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"go.ipao.vip/atomctl/templates"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"go.ipao.vip/atomctl/pkg/utils/gomod"
|
||||
"go.ipao.vip/atomctl/templates"
|
||||
)
|
||||
|
||||
// 验证包名是否合法:支持域名、路径分隔符和常见字符
|
||||
@@ -34,10 +36,32 @@ func CommandNewProject(root *cobra.Command) {
|
||||
}
|
||||
|
||||
func commandNewProjectE(cmd *cobra.Command, args []string) error {
|
||||
moduleName := args[0]
|
||||
var (
|
||||
moduleName string
|
||||
inPlace bool
|
||||
)
|
||||
|
||||
// shared flags
|
||||
dryRun, _ := cmd.Flags().GetBool("dry-run")
|
||||
baseDir, _ := cmd.Flags().GetString("dir")
|
||||
|
||||
if len(args) == 0 {
|
||||
if _, err := os.Stat("go.mod"); err == nil {
|
||||
pwd, _ := os.Getwd()
|
||||
if err := gomod.Parse(filepath.Join(pwd, "go.mod")); err != nil {
|
||||
return fmt.Errorf("parse go.mod failed: %v", err)
|
||||
}
|
||||
moduleName = gomod.GetModuleName()
|
||||
inPlace = true
|
||||
} else {
|
||||
return fmt.Errorf("module name required or run inside an existing module (go.mod)")
|
||||
}
|
||||
} else {
|
||||
moduleName = args[0]
|
||||
if !isValidGoPackageName(moduleName) {
|
||||
return fmt.Errorf("invalid module name: %s, should be a valid go package name", moduleName)
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("创建项目: ", moduleName)
|
||||
|
||||
@@ -50,90 +74,126 @@ func commandNewProjectE(cmd *cobra.Command, args []string) error {
|
||||
moduleSplitInfo := strings.Split(projectInfo.ModuleName, "/")
|
||||
projectInfo.ProjectName = moduleSplitInfo[len(moduleSplitInfo)-1]
|
||||
|
||||
// 检查目录是否存在
|
||||
force, _ := cmd.Flags().GetBool("force")
|
||||
if _, err := os.Stat(projectInfo.ProjectName); err == nil {
|
||||
|
||||
rootDir := "."
|
||||
if !inPlace {
|
||||
// honor base dir when creating a new project
|
||||
rootDir = filepath.Join(baseDir, projectInfo.ProjectName)
|
||||
if _, err := os.Stat(rootDir); err == nil {
|
||||
if !force {
|
||||
return fmt.Errorf("project directory %s already exists", projectInfo.ProjectName)
|
||||
return fmt.Errorf("project directory %s already exists", rootDir)
|
||||
}
|
||||
log.Warnf("强制删除已存在的目录: %s", projectInfo.ProjectName)
|
||||
if err := os.RemoveAll(projectInfo.ProjectName); err != nil {
|
||||
log.Warnf("强制删除已存在的目录: %s", rootDir)
|
||||
if dryRun {
|
||||
log.Infof("[dry-run] 将删除目录: %s", rootDir)
|
||||
} else if err := os.RemoveAll(rootDir); err != nil {
|
||||
return fmt.Errorf("failed to remove existing directory: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 创建项目根目录
|
||||
if err := os.MkdirAll(projectInfo.ProjectName, 0o755); err != nil {
|
||||
if dryRun {
|
||||
log.Infof("[dry-run] 将创建目录: %s", rootDir)
|
||||
} else if err := os.MkdirAll(rootDir, 0o755); err != nil {
|
||||
return fmt.Errorf("failed to create project directory: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 遍历和处理模板文件
|
||||
if err := fs.WalkDir(templates.Project, "project", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 计算相对路径,并处理隐藏文件
|
||||
relPath, err := filepath.Rel("project", path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 如果是隐藏文件模板,将文件名中的前缀 "-" 替换为 "."
|
||||
fileName := filepath.Base(relPath)
|
||||
if strings.HasPrefix(fileName, "-") {
|
||||
fileName = "." + strings.TrimPrefix(fileName, "-")
|
||||
relPath = filepath.Join(filepath.Dir(relPath), fileName)
|
||||
}
|
||||
|
||||
targetPath := filepath.Join(projectInfo.ProjectName, relPath)
|
||||
targetPath := filepath.Join(rootDir, relPath)
|
||||
if d.IsDir() {
|
||||
log.Infof("创建目录: %s", targetPath)
|
||||
if dryRun {
|
||||
log.Infof("[dry-run] mkdir -p %s", targetPath)
|
||||
return nil
|
||||
}
|
||||
return os.MkdirAll(targetPath, 0o755)
|
||||
}
|
||||
|
||||
// 读取模板内容
|
||||
content, err := templates.Project.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 处理模板文件
|
||||
// 渲染判定:优先(.tpl/.raw) > 行内指令 > 模板分隔符
|
||||
isTpl := false
|
||||
if strings.HasSuffix(path, ".tpl") {
|
||||
isTpl = true
|
||||
targetPath = strings.TrimSuffix(targetPath, ".tpl")
|
||||
} else if strings.HasSuffix(path, ".raw") {
|
||||
isTpl = false
|
||||
targetPath = strings.TrimSuffix(targetPath, ".raw")
|
||||
} else if bytes.Contains(content, []byte("atomctl:mode=tpl")) {
|
||||
isTpl = true
|
||||
} else if bytes.Contains(content, []byte("atomctl:mode=raw")) {
|
||||
isTpl = false
|
||||
} else if bytes.Contains(content, []byte("{{")) && bytes.Contains(content, []byte("}}")) {
|
||||
isTpl = true
|
||||
}
|
||||
|
||||
if inPlace && strings.HasSuffix(path, string(os.PathSeparator)+"go.mod.tpl") {
|
||||
if _, err := os.Stat(filepath.Join(rootDir, "go.mod")); err == nil {
|
||||
log.Infof("跳过已有文件: %s", filepath.Join(rootDir, "go.mod"))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if !force {
|
||||
if _, err := os.Stat(targetPath); err == nil {
|
||||
log.Warnf("文件已存在,跳过: %s", targetPath)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if isTpl {
|
||||
tmpl, err := template.New(filepath.Base(path)).Parse(string(content))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建目标文件(去除.tpl后缀)
|
||||
targetPath = strings.TrimSuffix(targetPath, ".tpl")
|
||||
log.Infof("创建文件: %s", targetPath)
|
||||
log.Infof("[渲染] 文件: %s", targetPath)
|
||||
if dryRun {
|
||||
log.Infof("[dry-run] render > %s", targetPath)
|
||||
return nil
|
||||
}
|
||||
f, err := os.Create(targetPath)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建文件失败 %s", targetPath)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return tmpl.Execute(f, projectInfo)
|
||||
}
|
||||
|
||||
// 处理模板文件
|
||||
if strings.HasSuffix(path, ".raw") {
|
||||
// 创建目标文件(去除.tpl后缀)
|
||||
targetPath = strings.TrimSuffix(targetPath, ".raw")
|
||||
log.Infof("创建文件: %s", targetPath)
|
||||
log.Infof("[复制] 文件: %s", targetPath)
|
||||
if dryRun {
|
||||
log.Infof("[dry-run] write > %s", targetPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 复制非模板文件
|
||||
return os.WriteFile(targetPath, content, 0o644)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 添加成功提示
|
||||
if inPlace {
|
||||
log.Info("🎉 项目初始化成功 (当前目录)!")
|
||||
} else {
|
||||
log.Info("🎉 项目创建成功!")
|
||||
log.Info("后续步骤:")
|
||||
log.Infof(" cd %s", projectInfo.ProjectName)
|
||||
}
|
||||
log.Info(" go mod tidy")
|
||||
|
||||
return nil
|
||||
|
||||
@@ -28,15 +28,23 @@ func CommandNewProvider(root *cobra.Command) {
|
||||
|
||||
func commandNewProviderE(cmd *cobra.Command, args []string) error {
|
||||
providerName := args[0]
|
||||
targetPath := filepath.Join("providers", providerName)
|
||||
// shared flags
|
||||
dryRun, _ := cmd.Flags().GetBool("dry-run")
|
||||
baseDir, _ := cmd.Flags().GetString("dir")
|
||||
|
||||
targetPath := filepath.Join(baseDir, "providers", providerName)
|
||||
|
||||
if _, err := os.Stat(targetPath); err == nil {
|
||||
return fmt.Errorf("目录 %s 已存在", targetPath)
|
||||
}
|
||||
|
||||
if dryRun {
|
||||
fmt.Printf("[dry-run] mkdir -p %s\n", targetPath)
|
||||
} else {
|
||||
if err := os.MkdirAll(targetPath, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := fs.WalkDir(templates.Provider, "provider", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
@@ -52,8 +60,12 @@ func commandNewProviderE(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
destPath := filepath.Join(targetPath, strings.TrimSuffix(relPath, ".tpl"))
|
||||
if dryRun {
|
||||
fmt.Printf("[dry-run] mkdir -p %s\n", filepath.Dir(destPath))
|
||||
} else {
|
||||
if err := os.MkdirAll(filepath.Dir(destPath), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
tmpl, err := template.ParseFS(templates.Provider, path)
|
||||
@@ -61,6 +73,11 @@ func commandNewProviderE(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if dryRun {
|
||||
fmt.Printf("[dry-run] render > %s\n", destPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
destFile, err := os.Create(destPath)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -13,13 +13,18 @@ func CommandSwagFmt(root *cobra.Command) {
|
||||
RunE: commandSwagFmtE,
|
||||
}
|
||||
|
||||
cmd.Flags().String("dir", "./app/http", "SearchDir for swag format")
|
||||
cmd.Flags().String("main", "main.go", "MainFile for swag format")
|
||||
|
||||
root.AddCommand(cmd)
|
||||
}
|
||||
|
||||
func commandSwagFmtE(cmd *cobra.Command, args []string) error {
|
||||
dir := cmd.Flag("dir").Value.String()
|
||||
main := cmd.Flag("main").Value.String()
|
||||
return format.New().Build(&format.Config{
|
||||
SearchDir: "./app/http",
|
||||
SearchDir: dir,
|
||||
Excludes: "",
|
||||
MainFile: "main.go",
|
||||
MainFile: main,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -18,27 +18,33 @@ func CommandSwagInit(root *cobra.Command) {
|
||||
RunE: commandSwagInitE,
|
||||
}
|
||||
|
||||
cmd.Flags().String("dir", ".", "SearchDir (project root)")
|
||||
cmd.Flags().String("out", "docs", "Output dir for generated docs")
|
||||
cmd.Flags().String("main", "main.go", "Main API file path")
|
||||
|
||||
root.AddCommand(cmd)
|
||||
}
|
||||
|
||||
func commandSwagInitE(cmd *cobra.Command, args []string) error {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(args) > 0 {
|
||||
pwd = args[0]
|
||||
root := cmd.Flag("dir").Value.String()
|
||||
if root == "" {
|
||||
var err error
|
||||
root, err = os.Getwd()
|
||||
if err != nil { return err }
|
||||
}
|
||||
|
||||
leftDelim, rightDelim := "{{", "}}"
|
||||
|
||||
outDir := cmd.Flag("out").Value.String()
|
||||
mainFile := cmd.Flag("main").Value.String()
|
||||
|
||||
return gen.New().Build(&gen.Config{
|
||||
SearchDir: pwd,
|
||||
SearchDir: root,
|
||||
Excludes: "",
|
||||
ParseExtension: "",
|
||||
MainAPIFile: "main.go",
|
||||
MainAPIFile: mainFile,
|
||||
PropNamingStrategy: swag.CamelCase,
|
||||
OutputDir: filepath.Join(pwd, "docs"),
|
||||
OutputDir: filepath.Join(root, outDir),
|
||||
OutputTypes: []string{"go", "json", "yaml"},
|
||||
ParseVendor: false,
|
||||
ParseDependency: 0,
|
||||
|
||||
@@ -29,6 +29,9 @@ type TableModelParam struct {
|
||||
PkgName string
|
||||
CamelTable string // user
|
||||
PascalTable string // User
|
||||
SoftDelete bool
|
||||
HasUpdatedAt bool
|
||||
HasCreatedAt bool
|
||||
}
|
||||
|
||||
func Generate(tables []string, transformer Transformer) error {
|
||||
@@ -65,6 +68,7 @@ func Generate(tables []string, transformer Transformer) error {
|
||||
return err
|
||||
}
|
||||
|
||||
modelContent := make(map[string]string)
|
||||
for _, file := range files {
|
||||
// get filename without ext
|
||||
name := strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))
|
||||
@@ -75,6 +79,13 @@ func Generate(tables []string, transformer Transformer) error {
|
||||
if err := os.Rename(from, to); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// read file content
|
||||
content, err := os.ReadFile(to)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
modelContent[name] = string(content)
|
||||
}
|
||||
|
||||
// remove database/schemas/public/model
|
||||
@@ -98,9 +109,32 @@ func Generate(tables []string, transformer Transformer) error {
|
||||
CamelTable: lo.CamelCase(table),
|
||||
PascalTable: lo.PascalCase(table),
|
||||
PkgName: gomod.GetModuleName(),
|
||||
SoftDelete: strings.Contains(modelContent[table], "DeletedAt"),
|
||||
HasUpdatedAt: strings.Contains(modelContent[table], "UpdatedAt"),
|
||||
HasCreatedAt: strings.Contains(modelContent[table], "CreatedAt"),
|
||||
}
|
||||
|
||||
items = append(items, tableInfo)
|
||||
|
||||
// tableFuncsFile
|
||||
tableFuncsFile := fmt.Sprintf("%s/%s.funcs.gen.go", baseDir, table)
|
||||
// 如果 modelFuncsFile 已存在,则跳过
|
||||
if _, err := os.Stat(tableFuncsFile); err == nil {
|
||||
fmt.Printf("Model funcs file %s already exists. Skipping...\n", tableFuncsFile)
|
||||
continue
|
||||
}
|
||||
|
||||
// 如果 modelFuncsFile 不存在,则创建
|
||||
fd, err := os.Create(tableFuncsFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create model funcs file %s: %w", tableFuncsFile, err)
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
if err := tableFuncsTpl.Execute(fd, tableInfo); err != nil {
|
||||
return fmt.Errorf("failed to render model funcs template: %w", err)
|
||||
}
|
||||
|
||||
modelFile := fmt.Sprintf("%s/%s.go", baseDir, table)
|
||||
// 如果 modelFile 已存在,则跳过
|
||||
if _, err := os.Stat(modelFile); err == nil {
|
||||
@@ -109,7 +143,7 @@ func Generate(tables []string, transformer Transformer) error {
|
||||
}
|
||||
|
||||
// 如果 modelFile 不存在,则创建
|
||||
fd, err := os.Create(modelFile)
|
||||
fd, err = os.Create(modelFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create model file %s: %w", modelFile, err)
|
||||
}
|
||||
@@ -137,24 +171,6 @@ func Generate(tables []string, transformer Transformer) error {
|
||||
return fmt.Errorf("failed to render model test template: %w", err)
|
||||
}
|
||||
|
||||
// tableFuncsFile
|
||||
tableFuncsFile := fmt.Sprintf("%s/%s.funcs.gen.go", baseDir, table)
|
||||
// 如果 modelFuncsFile 已存在,则跳过
|
||||
if _, err := os.Stat(tableFuncsFile); err == nil {
|
||||
fmt.Printf("Model funcs file %s already exists. Skipping...\n", tableFuncsFile)
|
||||
continue
|
||||
}
|
||||
|
||||
// 如果 modelFuncsFile 不存在,则创建
|
||||
fd, err = os.Create(tableFuncsFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create model funcs file %s: %w", tableFuncsFile, err)
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
if err := tableFuncsTpl.Execute(fd, tableInfo); err != nil {
|
||||
return fmt.Errorf("failed to render model funcs template: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染总的 provider 文件
|
||||
|
||||
@@ -6,12 +6,75 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
|
||||
{{ if gt (len .) 0 }}
|
||||
"{{ (index . 0).PkgName }}/database/table"
|
||||
{{ end }}
|
||||
|
||||
"go.ipao.vip/atom"
|
||||
"go.ipao.vip/atom/container"
|
||||
"go.ipao.vip/atom/contracts"
|
||||
"go.ipao.vip/atom/opt"
|
||||
. "github.com/go-jet/jet/v2/postgres"
|
||||
"github.com/samber/lo"
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
type Cond func(BoolExpression) BoolExpression
|
||||
|
||||
func ExprCond(expr BoolExpression) Cond {
|
||||
return func(cond BoolExpression) BoolExpression {
|
||||
return cond.AND(expr)
|
||||
}
|
||||
}
|
||||
|
||||
func CondTrue(conds ...Cond) BoolExpression {
|
||||
cond:= BoolExp(Bool(true))
|
||||
for _, c := range conds {
|
||||
cond = c(cond)
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
func CondJoin(cond Cond, conds ...Cond) []Cond {
|
||||
return append([]Cond{cond}, conds...)
|
||||
}
|
||||
|
||||
// converts
|
||||
func IntExprSlice[T constraints.Integer](slice []T) []Expression {
|
||||
if len(slice) == 0 { return nil }
|
||||
|
||||
return lo.Map(slice, func(item T, _ int) Expression {
|
||||
switch any(item).(type) {
|
||||
case int8:
|
||||
return Int8(int8(item))
|
||||
case int16:
|
||||
return Int16(int16(item))
|
||||
case int32:
|
||||
return Int32(int32(item))
|
||||
case int64:
|
||||
return Int64(int64(item))
|
||||
case uint8:
|
||||
return Uint8(uint8(item))
|
||||
case uint16:
|
||||
return Uint16(uint16(item))
|
||||
case uint32:
|
||||
return Uint32(uint32(item))
|
||||
case uint64:
|
||||
return Uint64(uint64(item))
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// tables
|
||||
{{- range . }}
|
||||
var tbl{{.PascalTable}} = table.{{.PascalTable}}
|
||||
{{- end }}
|
||||
|
||||
|
||||
// models
|
||||
var db *sql.DB
|
||||
{{- range . }}
|
||||
func {{.PascalTable}}Model() *{{.PascalTable}} { return &{{.PascalTable}}{} }
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
var tbl{{.PascalTable}}UpdateMutableColumns = tbl{{.PascalTable}}.MutableColumns.Except(
|
||||
{{- if .HasCreatedAt}}
|
||||
tbl{{.PascalTable}}.CreatedAt,
|
||||
{{- end}}
|
||||
|
||||
func (m *{{.PascalTable}}) log() *log.Entry {
|
||||
return log.WithField("model", "{{.PascalTable}}Model")
|
||||
}
|
||||
{{- if .SoftDelete}}
|
||||
tbl{{.PascalTable}}.DeletedAt,
|
||||
{{- end}}
|
||||
)
|
||||
|
||||
@@ -1,18 +1,52 @@
|
||||
// Code generated by the atomctl ; DO NOT EDIT.
|
||||
// Code generated by the atomctl ; DO NOT EDIT.
|
||||
// Code generated by the atomctl ; DO NOT EDIT.
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
"github.com/samber/lo"
|
||||
. "github.com/go-jet/jet/v2/postgres"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
// conds
|
||||
{{- if .SoftDelete }}
|
||||
func (m *{{.PascalTable}}) CondNotDeleted() Cond {
|
||||
return func(cond BoolExpression) BoolExpression {
|
||||
return cond.AND(tbl{{.PascalTable}}.DeletedAt.IS_NULL())
|
||||
}
|
||||
}
|
||||
|
||||
func (m *{{.PascalTable}}) CondDeleted() Cond {
|
||||
return func(cond BoolExpression) BoolExpression {
|
||||
return cond.AND(tbl{{.PascalTable}}.DeletedAt.IS_NOT_NULL())
|
||||
}
|
||||
}
|
||||
{{- end}}
|
||||
|
||||
func (m *{{.PascalTable}}) CondID(id int64) Cond {
|
||||
return func(cond BoolExpression) BoolExpression {
|
||||
return cond.AND(tbl{{.PascalTable}}.ID.EQ(Int(id)))
|
||||
}
|
||||
}
|
||||
|
||||
// funcs
|
||||
func (m *{{.PascalTable}}) log() *log.Entry {
|
||||
return log.WithField("model", "{{.PascalTable}}")
|
||||
}
|
||||
|
||||
func (m *{{.PascalTable}}) Create(ctx context.Context) error {
|
||||
{{- if .HasCreatedAt}}
|
||||
m.CreatedAt = time.Now()
|
||||
stmt := table.Medias.INSERT(table.{{.PascalTable}}.MutableColumns).MODEL(m).RETURNING(table.Medias.AllColumns)
|
||||
{{- end}}
|
||||
|
||||
{{- if .HasUpdatedAt}}
|
||||
m.UpdatedAt = time.Now()
|
||||
{{- end}}
|
||||
|
||||
|
||||
stmt := tbl{{.PascalTable}}.INSERT(tbl{{.PascalTable}}.MutableColumns).MODEL(m).RETURNING(tbl{{.PascalTable}}.AllColumns)
|
||||
m.log().WithField("func","Create").Info( stmt.DebugSql())
|
||||
|
||||
if err := stmt.QueryContext(ctx, db, m); err != nil {
|
||||
@@ -26,7 +60,7 @@ func (m *{{.PascalTable}}) Create(ctx context.Context) error {
|
||||
|
||||
|
||||
func (m *{{.PascalTable}}) BatchCreate(ctx context.Context, models []*{{.PascalTable}}) error {
|
||||
stmt := table.{{.PascalTable}}.INSERT(table.{{.PascalTable}}.MutableColumns).MODELS(models)
|
||||
stmt := tbl{{.PascalTable}}.INSERT(tbl{{.PascalTable}}.MutableColumns).MODELS(models)
|
||||
m.log().WithField("func", "BatchCreate").Info(stmt.DebugSql())
|
||||
|
||||
if _, err := stmt.ExecContext(ctx, db); err != nil {
|
||||
@@ -38,9 +72,41 @@ func (m *{{.PascalTable}}) BatchCreate(ctx context.Context, models []*{{.PascalT
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete
|
||||
{{- if .SoftDelete }}
|
||||
func (m *{{.PascalTable}}) Delete(ctx context.Context) error {
|
||||
stmt := table.{{.PascalTable}}.DELETE().WHERE(table.{{.PascalTable}}.ID.EQ(m.ID))
|
||||
stmt := tbl{{.PascalTable}}.UPDATE().SET(tbl{{.PascalTable}}.DeletedAt.SET(TimestampT(time.Now()))).WHERE(tbl{{.PascalTable}}.ID.EQ(Int(m.ID)))
|
||||
m.log().WithField("func", "SoftDelete").Info(stmt.DebugSql())
|
||||
|
||||
if err := stmt.QueryContext(ctx, db, m); err != nil {
|
||||
m.log().WithField("func","SoftDelete").Errorf("error soft deleting {{.PascalTable}} item: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
m.log().WithField("func", "SoftDelete").Infof("{{.PascalTable}} item soft deleted successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// BatchDelete
|
||||
func (m *{{.PascalTable}}) BatchDelete(ctx context.Context, ids []int64) error {
|
||||
condIds := lo.Map(ids, func(id int64, _ int) Expression {
|
||||
return Int64(id)
|
||||
})
|
||||
|
||||
stmt := tbl{{.PascalTable}}.UPDATE().SET(tbl{{.PascalTable}}.DeletedAt.SET(TimestampT(time.Now()))).WHERE(tbl{{.PascalTable}}.ID.IN(condIds...))
|
||||
m.log().WithField("func", "BatchSoftDelete").Info(stmt.DebugSql())
|
||||
|
||||
if err := stmt.QueryContext(ctx, db, m); err != nil {
|
||||
m.log().WithField("func","BatchSoftDelete").Errorf("error soft deleting {{.PascalTable}} items: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
m.log().WithField("func", "BatchSoftDelete").WithField("ids", ids).Infof("{{.PascalTable}} items soft deleted successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func (m *{{.PascalTable}}) ForceDelete(ctx context.Context) error {
|
||||
stmt := tbl{{.PascalTable}}.DELETE().WHERE(tbl{{.PascalTable}}.ID.EQ(Int(m.ID)))
|
||||
m.log().WithField("func", "Delete").Info(stmt.DebugSql())
|
||||
|
||||
if _, err := stmt.ExecContext(ctx, db); err != nil {
|
||||
@@ -52,21 +118,61 @@ func (m *{{.PascalTable}}) Delete(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *{{.PascalTable}}) BatchDelete(ctx context.Context, ids []int64) error {
|
||||
stmt := table.{{.PascalTable}}.DELETE().WHERE(table.{{.PascalTable}}.ID.IN(ids))
|
||||
func (m *{{.PascalTable}}) BatchForceDelete(ctx context.Context, ids []int64) error {
|
||||
condIds := lo.Map(ids, func(id int64, _ int) Expression {
|
||||
return Int64(id)
|
||||
})
|
||||
|
||||
stmt := tbl{{.PascalTable}}.DELETE().WHERE(tbl{{.PascalTable}}.ID.IN(condIds...))
|
||||
m.log().WithField("func", "BatchDelete").Info(stmt.DebugSql())
|
||||
|
||||
if _, err := stmt.ExecContext(ctx, db); err != nil {
|
||||
m.log().WithField("func","BatchForceDelete").Errorf("error deleting {{.PascalTable}} items: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
m.log().WithField("func", "BatchForceDelete").WithField("ids", ids).Infof("{{.PascalTable}} items deleted successfully")
|
||||
return nil
|
||||
}
|
||||
{{- else}}
|
||||
func (m *{{.PascalTable}}) Delete(ctx context.Context) error {
|
||||
stmt := tbl{{.PascalTable}}.DELETE().WHERE(tbl{{.PascalTable}}.ID.EQ(Int(m.ID)))
|
||||
m.log().WithField("func", "Delete").Info(stmt.DebugSql())
|
||||
|
||||
if err := stmt.QueryContext(ctx, db, m); err != nil {
|
||||
m.log().WithField("func","Delete").Errorf("error deleting {{.PascalTable}} item: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
m.log().WithField("func", "Delete").Infof("{{.PascalTable}} item deleted successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// BatchDelete
|
||||
func (m *{{.PascalTable}}) BatchDelete(ctx context.Context, ids []int64) error {
|
||||
condIds := lo.Map(ids, func(id int64, _ int) Expression {
|
||||
return Int64(id)
|
||||
})
|
||||
|
||||
stmt := tbl{{.PascalTable}}.DELETE().WHERE(tbl{{.PascalTable}}.ID.IN(condIds...))
|
||||
m.log().WithField("func", "BatchDelete").Info(stmt.DebugSql())
|
||||
|
||||
if err := stmt.QueryContext(ctx, db, m); err != nil {
|
||||
m.log().WithField("func","BatchDelete").Errorf("error deleting {{.PascalTable}} items: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
m.log().WithField("func", "BatchDelete").Infof("{{.PascalTable}} items deleted successfully")
|
||||
m.log().WithField("func", "BatchDelete").WithField("ids", ids).Infof("{{.PascalTable}} items deleted successfully")
|
||||
return nil
|
||||
}
|
||||
{{- end}}
|
||||
|
||||
func (m *{{.PascalTable}}) Update(ctx context.Context) error {
|
||||
stmt := table.{{.PascalTable}}.UPDATE(table.{{.PascalTable}}.MutableColumns).SET(m).WHERE(table.{{.PascalTable}}.ID.EQ(m.ID)).RETURNING(table.{{.PascalTable}}.AllColumns)
|
||||
{{- if .HasUpdatedAt}}
|
||||
m.UpdatedAt = time.Now()
|
||||
{{- end}}
|
||||
|
||||
stmt := tbl{{.PascalTable}}.UPDATE(tbl{{.PascalTable}}UpdateMutableColumns).MODEL(m).WHERE(tbl{{.PascalTable}}.ID.EQ(Int(m.ID))).RETURNING(tbl{{.PascalTable}}.AllColumns)
|
||||
m.log().WithField("func", "Update").Info(stmt.DebugSql())
|
||||
|
||||
if err := stmt.QueryContext(ctx, db, m); err != nil {
|
||||
@@ -77,32 +183,34 @@ func (m *{{.PascalTable}}) Update(ctx context.Context) error {
|
||||
m.log().WithField("func", "Update").Infof("{{.PascalTable}} item updated successfully")
|
||||
return nil
|
||||
}
|
||||
// GetByID
|
||||
func (m *{{.PascalTable}}) GetByID(ctx context.Context, id int64) (*{{.PascalTable}}, error) {
|
||||
stmt := table.{{.PascalTable}}.SELECT(table.{{.PascalTable}}.AllColumns).WHERE(table.{{.PascalTable}}.ID.EQ(id))
|
||||
m.log().WithField("func", "GetByID").Info(stmt.DebugSql())
|
||||
|
||||
// GetByCond
|
||||
func (m *{{.PascalTable}}) GetByCond(ctx context.Context, conds ...Cond) (*{{.PascalTable}}, error) {
|
||||
cond := CondTrue(conds...)
|
||||
|
||||
stmt := tbl{{.PascalTable}}.SELECT(tbl{{.PascalTable}}.AllColumns).WHERE(cond)
|
||||
m.log().WithField("func", "GetByCond").Info(stmt.DebugSql())
|
||||
|
||||
if err := stmt.QueryContext(ctx, db, m); err != nil {
|
||||
m.log().WithField("func","GetByID").Errorf("error getting {{.PascalTable}} item by ID: %v", err)
|
||||
m.log().WithField("func","GetByCond").Errorf("error getting {{.PascalTable}} item by ID: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.log().WithField("func", "GetByID").Infof("{{.PascalTable}} item retrieved successfully")
|
||||
m.log().WithField("func", "GetByCond").Infof("{{.PascalTable}} item retrieved successfully")
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// GetByID
|
||||
func (m *{{.PascalTable}}) GetByID(ctx context.Context, id int64, conds ...Cond) (*{{.PascalTable}}, error) {
|
||||
return m.GetByCond(ctx, CondJoin(m.CondID(id), conds...)...)
|
||||
}
|
||||
|
||||
|
||||
// Count
|
||||
func (m *{{.PascalTable}}) Count(ctx context.Context, conds ...BoolExpression) (int64, error) {
|
||||
cond := Bool(true)
|
||||
if len(conds) > 0 {
|
||||
for _, c := range conds {
|
||||
cond = cond.AND(c)
|
||||
}
|
||||
}
|
||||
func (m *{{.PascalTable}}) Count(ctx context.Context, conds ...Cond) (int64, error) {
|
||||
cond := CondTrue(conds...)
|
||||
|
||||
tbl := table.{{.PascalTable}}
|
||||
stmt := tbl.SELECT(COUNT(tbl.ID).AS("count")).WHERE(cond)
|
||||
stmt := tbl{{.PascalTable}}.SELECT(COUNT(tbl{{.PascalTable}}.ID).AS("count")).WHERE(cond)
|
||||
m.log().Infof("sql: %s", stmt.DebugSql())
|
||||
|
||||
var count struct {
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"{{ .PkgName }}/app/service/testx"
|
||||
"{{ .PkgName }}/database"
|
||||
"{{ .PkgName }}/database/schemas/public/table"
|
||||
"{{ .PkgName }}/database/table"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"go.ipao.vip/atom/contracts"
|
||||
@@ -38,6 +38,6 @@ func Test_{{ .PascalTable }}(t *testing.T) {
|
||||
|
||||
func (s *{{ .PascalTable }}TestSuite) Test_Demo() {
|
||||
Convey("Test_Demo", s.T(), func() {
|
||||
database.Truncate(context.Background(), db, table.{{ .PascalTable }}.TableName())
|
||||
database.Truncate(context.Background(), db, tbl{{ .PascalTable }}.TableName())
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user