gorig-cli 1.0.24 → 1.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -40
  3. package/commands/create.js +267 -112
  4. package/commands/doc.js +21 -8
  5. package/commands/init.js +316 -255
  6. package/commands/skill.js +55 -17
  7. package/package.json +1 -1
  8. package/templates/config.go.ejs +6 -5
  9. package/templates/config.yaml.ejs +3 -38
  10. package/templates/controller.go.ejs +11 -42
  11. package/templates/crud.controller.go.ejs +71 -0
  12. package/templates/crud.doc.md.ejs +60 -0
  13. package/templates/crud.dto.go.ejs +103 -0
  14. package/templates/crud.integration.init_test.go.ejs +17 -0
  15. package/templates/crud.integration_test.go.ejs +66 -0
  16. package/templates/crud.model.go.ejs +25 -0
  17. package/templates/crud.module.README.md.ejs +70 -0
  18. package/templates/crud.mongo.config.yaml.ejs +19 -0
  19. package/templates/crud.mysql.config.yaml.ejs +14 -0
  20. package/templates/crud.router.go.ejs +18 -0
  21. package/templates/crud.service.go.ejs +179 -0
  22. package/templates/crud.test.go.ejs +52 -0
  23. package/templates/dto.go.ejs +16 -0
  24. package/templates/gitignore.ejs +18 -49
  25. package/templates/hello.controller.go.ejs +19 -0
  26. package/templates/hello.router.go.ejs +12 -0
  27. package/templates/hello.service.go.ejs +31 -0
  28. package/templates/hello.test.go.ejs +34 -0
  29. package/templates/init.go.ejs +1 -27
  30. package/templates/main.go.ejs +2 -5
  31. package/templates/model.go.ejs +3 -4
  32. package/templates/project.README.md.ejs +35 -0
  33. package/templates/router.go.ejs +4 -8
  34. package/templates/service.go.ejs +25 -77
  35. package/templates/skills/gorig-backend/SKILL.md +89 -0
  36. package/templates/skills/gorig-backend/agents/openai.yaml +4 -0
  37. package/templates/skills/gorig-backend/assets/api-doc-template.md +59 -0
  38. package/templates/skills/gorig-backend/assets/gorig.gitignore +65 -0
  39. package/templates/skills/gorig-backend/assets/module-readme-template.md +58 -0
  40. package/templates/skills/gorig-backend/references/advanced-data-access.md +275 -0
  41. package/templates/skills/gorig-backend/references/auth-security.md +194 -0
  42. package/templates/skills/gorig-backend/references/business-scenarios.md +155 -0
  43. package/templates/skills/gorig-backend/references/cache.md +301 -0
  44. package/templates/skills/gorig-backend/references/capability-matrix.md +37 -0
  45. package/templates/skills/gorig-backend/references/configuration.md +48 -0
  46. package/templates/skills/gorig-backend/references/framework-api.md +190 -0
  47. package/templates/skills/gorig-backend/references/messaging.md +143 -0
  48. package/templates/skills/gorig-backend/references/onboarding-files.md +46 -0
  49. package/templates/skills/gorig-backend/references/outbound-http.md +162 -0
  50. package/templates/skills/gorig-backend/references/persistent-crud.md +332 -0
  51. package/templates/skills/gorig-backend/references/project-bootstrap.md +128 -0
  52. package/templates/skills/gorig-backend/references/scheduled-tasks.md +231 -0
  53. package/templates/skills/gorig-backend/references/service-lifecycle.md +51 -0
  54. package/templates/skills/gorig-backend/references/source-map.md +43 -0
  55. package/templates/skills/gorig-backend/references/source-policy.md +58 -0
  56. package/templates/skills/gorig-backend/references/sse.md +121 -0
  57. package/templates/skills/gorig-backend/references/testing.md +171 -0
  58. package/templates/skills/gorig-backend/scripts/check-source-links.sh +48 -0
  59. package/templates/skills/gorig-backend/scripts/detect-gorig-context.sh +67 -0
  60. package/templates/skills/gorig-backend/scripts/verify-basic-project.sh +108 -0
  61. package/npm_publish copy.sh +0 -65
  62. package/templates/internal.go.ejs +0 -49
  63. package/templates/req.go.ejs +0 -8
  64. package/templates/resp.go.ejs +0 -8
  65. package/templates/service.pub.go.ejs +0 -22
  66. package/templates/skills/claude/gorig-backend/SKILL.md +0 -766
  67. package/templates/skills/claude/gorig-backend/assets/api-doc-template.md +0 -50
  68. package/templates/skills/claude/gorig-backend/assets/module-readme-template.md +0 -46
  69. package/templates/skills/claude/gorig-backend/references/onboarding-files.md +0 -44
  70. package/templates/skills/codex/gorig-backend/SKILL.md +0 -766
  71. package/templates/skills/codex/gorig-backend/agents/openai.yaml +0 -4
  72. package/templates/skills/codex/gorig-backend/assets/api-doc-template.md +0 -50
  73. package/templates/skills/codex/gorig-backend/assets/module-readme-template.md +0 -46
  74. package/templates/skills/codex/gorig-backend/references/onboarding-files.md +0 -44
package/commands/skill.js CHANGED
@@ -15,41 +15,73 @@ const printUsage = () => {
15
15
  console.log(chalk.yellow('Usage: gorig-cli skill install <codex|claude|all> [user|project]'));
16
16
  };
17
17
 
18
- const installSkill = async (target, scope) => {
19
- const templateRoot = path.join(__dirname, '../templates/skills');
18
+ export const resolveInstalls = (target, scope, options = {}) => {
19
+ const cwd = options.cwd || process.cwd();
20
+ const home = options.home || os.homedir();
21
+ const templateRoot = options.templateRoot || path.join(__dirname, '../templates/skills');
22
+ const source = path.join(templateRoot, SKILL_NAME);
20
23
  const installs = [];
21
24
 
22
25
  if (target === 'codex' || target === 'all') {
23
- const codexHome = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
26
+ const codexBase = scope === 'project'
27
+ ? path.join(cwd, '.agents')
28
+ : path.join(home, '.agents');
29
+
24
30
  installs.push({
25
- label: 'Codex',
26
- source: path.join(templateRoot, 'codex', SKILL_NAME),
27
- destination: path.join(codexHome, 'skills', SKILL_NAME),
31
+ label: `Codex (${scope})`,
32
+ source,
33
+ destination: path.join(codexBase, 'skills', SKILL_NAME),
28
34
  });
29
35
  }
30
36
 
31
37
  if (target === 'claude' || target === 'all') {
32
38
  const claudeBase = scope === 'project'
33
- ? path.join(process.cwd(), '.claude')
34
- : path.join(os.homedir(), '.claude');
39
+ ? path.join(cwd, '.claude')
40
+ : path.join(home, '.claude');
35
41
 
36
42
  installs.push({
37
43
  label: `Claude (${scope})`,
38
- source: path.join(templateRoot, 'claude', SKILL_NAME),
44
+ source,
39
45
  destination: path.join(claudeBase, 'skills', SKILL_NAME),
40
46
  });
41
47
  }
42
48
 
49
+ return installs;
50
+ };
51
+
52
+ export const installSkill = async (target, scope, options = {}) => {
53
+ const installs = resolveInstalls(target, scope, options);
54
+
43
55
  for (const install of installs) {
44
56
  const exists = await fs.pathExists(install.source);
45
57
  if (!exists) {
46
58
  throw new Error(`Skill template not found: ${install.source}`);
47
59
  }
60
+ }
61
+
62
+ const staged = [];
63
+
64
+ try {
65
+ for (const install of installs) {
66
+ const temporary = `${install.destination}.tmp-${process.pid}-${Date.now()}`;
67
+ await fs.remove(temporary);
68
+ await fs.ensureDir(path.dirname(temporary));
69
+ await fs.copy(install.source, temporary, { overwrite: true });
70
+ staged.push({ ...install, temporary });
71
+ }
48
72
 
49
- await fs.ensureDir(path.dirname(install.destination));
50
- await fs.copy(install.source, install.destination, { overwrite: true });
51
- console.log(chalk.green(`Installed ${install.label} skill to ${install.destination}`));
73
+ for (const install of staged) {
74
+ await fs.ensureDir(path.dirname(install.destination));
75
+ await fs.remove(install.destination);
76
+ await fs.move(install.temporary, install.destination);
77
+ console.log(chalk.green(`Installed ${install.label} skill to ${install.destination}`));
78
+ }
79
+ } catch (error) {
80
+ await Promise.all(staged.map(({ temporary }) => fs.remove(temporary)));
81
+ throw error;
52
82
  }
83
+
84
+ return installs;
53
85
  };
54
86
 
55
87
  const skillModule = async (args) => {
@@ -57,13 +89,15 @@ const skillModule = async (args) => {
57
89
 
58
90
  if (!action) {
59
91
  printUsage();
60
- process.exit(1);
92
+ process.exitCode = 1;
93
+ return 1;
61
94
  }
62
95
 
63
96
  if (action !== 'install') {
64
97
  console.error(chalk.red(`Unknown skill action: ${action}`));
65
98
  printUsage();
66
- process.exit(1);
99
+ process.exitCode = 1;
100
+ return 1;
67
101
  }
68
102
 
69
103
  const target = args[1] || 'all';
@@ -72,20 +106,24 @@ const skillModule = async (args) => {
72
106
  if (!VALID_TARGETS.has(target)) {
73
107
  console.error(chalk.red(`Unknown skill target: ${target}`));
74
108
  printUsage();
75
- process.exit(1);
109
+ process.exitCode = 1;
110
+ return 1;
76
111
  }
77
112
 
78
113
  if (!VALID_SCOPES.has(scope)) {
79
114
  console.error(chalk.red(`Unknown skill scope: ${scope}`));
80
115
  printUsage();
81
- process.exit(1);
116
+ process.exitCode = 1;
117
+ return 1;
82
118
  }
83
119
 
84
120
  try {
85
121
  await installSkill(target, scope);
122
+ return 0;
86
123
  } catch (error) {
87
124
  console.error(chalk.red('Failed to install skill:'), chalk.redBright(error.message));
88
- process.exit(1);
125
+ process.exitCode = 1;
126
+ return 1;
89
127
  }
90
128
  };
91
129
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gorig-cli",
3
- "version": "1.0.24",
3
+ "version": "1.0.26",
4
4
  "type": "module",
5
5
  "description": "gorig build tool",
6
6
  "main": "bin/cli.js",
@@ -1,14 +1,15 @@
1
1
  package global
2
2
 
3
- import (
4
- configure "github.com/jom-io/gorig/utils/cofigure"
5
- )
3
+ import configure "github.com/jom-io/gorig/utils/cofigure"
6
4
 
7
5
  var (
8
- DBDef = "def"
9
6
  SysName = ""
7
+ RunMode = ""
8
+ RestAddr = ""
10
9
  )
11
10
 
12
11
  func init() {
13
- SysName = configure.GetString("sys.name", "TEST")
12
+ SysName = configure.GetString("sys.name", "<%= projectNameUpper %>")
13
+ RunMode = configure.GetString("sys.mode", "local")
14
+ RestAddr = configure.GetString("api.rest.addr", ":<%= localPort %>")
14
15
  }
@@ -4,42 +4,7 @@ sys:
4
4
 
5
5
  api:
6
6
  rest:
7
- addr: :9527
7
+ addr: ":<%= port %>"
8
8
 
9
- #db:
10
- # prefix: <%= projectPrefix %>_
11
-
12
- #mongo:
13
- # Main:
14
- # uri: mongodb://addr
15
- # auth:
16
- # need: true
17
- # source: db_source
18
- # user: user
19
- # password: pwd
20
- # db:
21
- # name: db_name
22
- #
23
- #Mysql:
24
- # Main:
25
- # GormInit: 1
26
- # SlowThreshold: 30 # Slow SQL threshold (sysql execution time exceeds this time unit (second), system logging will be triggered)
27
- # Write:
28
- # Host: addr
29
- # DataBase: db_name
30
- # Port: 3306
31
- # User: user
32
- # Pass: pwd
33
- # Charset: "utf8"
34
- # SetMaxIdleConns: 10
35
- # SetMaxOpenConns: 128
36
- # SetConnMaxLifetime: 60 # Maximum survival time when connection is inactive (seconds)
37
-
38
- #redis:
39
- # addr: localhost:6379
40
- # password:
41
- # db: 0
42
-
43
- #notify:
44
- # dingding:
45
- # token: xxxxx
9
+ # Add database, Redis, and notification configuration only when the project uses them.
10
+ # Prefer GORIG_<SECTION>_<KEY> environment variables for secrets.
@@ -4,59 +4,28 @@ import (
4
4
  "github.com/gin-gonic/gin"
5
5
  "github.com/jom-io/gorig/apix"
6
6
  "github.com/jom-io/gorig/global/consts"
7
- "<%= projectName %>/domain/<%= moduleName %>/api/req"
8
7
  )
9
8
 
10
- func Page<%= ModuleName %>(ctx *gin.Context) {
9
+ func Info(ctx *gin.Context) {
11
10
  defer apix.HandlePanic(ctx)
12
- pageReq, e := apix.GetPageReq(ctx)
13
- filter := &req.<%= ModuleName %>Req{}
14
- e = apix.BindParams(ctx, filter)
15
- if e != nil {
16
- return
17
- }
18
- result, err := gService.Page<%= ModuleName %>(ctx, pageReq, filter)
19
- apix.HandleData(ctx, consts.CurdSelectFailCode, &result, err)
20
- }
21
11
 
22
- func List<%= ModuleName %>(ctx *gin.Context) {
23
- defer apix.HandlePanic(ctx)
24
- filter := &req.<%= ModuleName %>Req{}
25
- e := apix.BindParams(ctx, filter)
26
- if e != nil {
12
+ name, err := apix.GetParamType[string](ctx, "name", apix.NotForce, "world")
13
+ if err != nil {
27
14
  return
28
15
  }
29
- result, err := gService.List<%= ModuleName %>(ctx, filter)
30
- apix.HandleData(ctx, consts.CurdSelectFailCode, &result, err)
31
- }
32
16
 
33
- func Save<%= ModuleName %>(ctx *gin.Context) {
34
- defer apix.HandlePanic(ctx)
35
- <%= moduleName %>Req := &req.<%= ModuleName %>Req{}
36
- e := apix.BindParams(ctx, <%= moduleName %>Req)
37
- if e != nil {
38
- return
39
- }
40
- err := gService.Save<%= ModuleName %>(ctx, <%= moduleName %>Req)
41
- apix.HandleData(ctx, consts.CurdUpdateFailCode, nil, err)
17
+ result, serviceErr := BuildInfo(ctx, name)
18
+ apix.HandleData(ctx, consts.CurdSelectFailCode, result, serviceErr)
42
19
  }
43
20
 
44
- func Get<%= ModuleName %>(ctx *gin.Context) {
21
+ func Echo(ctx *gin.Context) {
45
22
  defer apix.HandlePanic(ctx)
46
- id, e := apix.GetParamInt64(ctx, "id", apix.Force)
47
- if e != nil {
48
- return
49
- }
50
- result, err := gService.Get<%= ModuleName %>(ctx, id)
51
- apix.HandleData(ctx, consts.CurdSelectFailCode, &result, err)
52
- }
53
23
 
54
- func Delete<%= ModuleName %>(ctx *gin.Context) {
55
- defer apix.HandlePanic(ctx)
56
- id, e := apix.GetParamInt64(ctx, "id", apix.Force)
57
- if e != nil {
24
+ req := &EchoReq{}
25
+ if err := apix.BindParams(ctx, req); err != nil {
58
26
  return
59
27
  }
60
- err := gService.Delete<%= ModuleName %>(ctx, id)
61
- apix.HandleData(ctx, consts.CurdUpdateFailCode, nil, err)
28
+
29
+ result, serviceErr := EchoMessage(ctx, req)
30
+ apix.HandleData(ctx, consts.CurdSelectFailCode, result, serviceErr)
62
31
  }
@@ -0,0 +1,71 @@
1
+ package <%= moduleName %>
2
+
3
+ import (
4
+ "github.com/gin-gonic/gin"
5
+ "github.com/jom-io/gorig/apix"
6
+ "github.com/jom-io/gorig/global/consts"
7
+ )
8
+
9
+ func CreateHTTP(ctx *gin.Context) {
10
+ defer apix.HandlePanic(ctx)
11
+ req := &CreateReq{}
12
+ if err := apix.BindParams(ctx, req); err != nil {
13
+ return
14
+ }
15
+ id, err := Create(ctx, req)
16
+ apix.HandleData(ctx, consts.CurdUpdateFailCode, id, err)
17
+ }
18
+
19
+ func InfoHTTP(ctx *gin.Context) {
20
+ defer apix.HandlePanic(ctx)
21
+ id, err := apix.GetParamInt64(ctx, "id", apix.Force)
22
+ if err != nil {
23
+ return
24
+ }
25
+ resp, serviceErr := Info(ctx, id)
26
+ apix.HandleData(ctx, consts.CurdSelectFailCode, resp, serviceErr)
27
+ }
28
+
29
+ func ListHTTP(ctx *gin.Context) {
30
+ defer apix.HandlePanic(ctx)
31
+ req := &QueryReq{}
32
+ if err := apix.BindParams(ctx, req); err != nil {
33
+ return
34
+ }
35
+ resp, serviceErr := List(ctx, req)
36
+ apix.HandleData(ctx, consts.CurdSelectFailCode, resp, serviceErr)
37
+ }
38
+
39
+ func PageHTTP(ctx *gin.Context) {
40
+ defer apix.HandlePanic(ctx)
41
+ pageReq, err := apix.GetPageReq(ctx)
42
+ if err != nil {
43
+ return
44
+ }
45
+ req := &QueryReq{}
46
+ if err = apix.BindParams(ctx, req); err != nil {
47
+ return
48
+ }
49
+ resp, serviceErr := Page(ctx, req, pageReq)
50
+ apix.HandleData(ctx, consts.CurdSelectFailCode, resp, serviceErr)
51
+ }
52
+
53
+ func UpdateHTTP(ctx *gin.Context) {
54
+ defer apix.HandlePanic(ctx)
55
+ req := &UpdateReq{}
56
+ if err := apix.BindParams(ctx, req); err != nil {
57
+ return
58
+ }
59
+ err := Update(ctx, req)
60
+ apix.HandleData(ctx, consts.CurdUpdateFailCode, nil, err)
61
+ }
62
+
63
+ func DeleteHTTP(ctx *gin.Context) {
64
+ defer apix.HandlePanic(ctx)
65
+ id, err := apix.GetParamInt64(ctx, "id", apix.Force)
66
+ if err != nil {
67
+ return
68
+ }
69
+ serviceErr := Delete(ctx, id)
70
+ apix.HandleData(ctx, consts.CurdUpdateFailCode, nil, serviceErr)
71
+ }
@@ -0,0 +1,60 @@
1
+ # <%= ModuleName %> API
2
+
3
+ Persistent CRUD HTTP API backed by <%= dbTitle %>.
4
+
5
+ ## POST /<%= moduleName %>/create
6
+
7
+ Request:
8
+
9
+ ```json
10
+ {
11
+ "name": "demo",
12
+ "status": "enabled",
13
+ "remark": "optional"
14
+ }
15
+ ```
16
+
17
+ Response data: created numeric ID.
18
+
19
+ ## GET /<%= moduleName %>/info
20
+
21
+ Query:
22
+
23
+ | Name | Required | Description |
24
+ |---|---|---|
25
+ | id | yes | Record ID |
26
+
27
+ ## GET /<%= moduleName %>/list
28
+
29
+ Optional filters: `name`, `status`.
30
+
31
+ ## GET /<%= moduleName %>/page
32
+
33
+ Optional filters: `name`, `status`, `page`, `size`, `lastID`.
34
+
35
+ ## POST /<%= moduleName %>/update
36
+
37
+ Request:
38
+
39
+ ```json
40
+ {
41
+ "id": 1,
42
+ "name": "new name",
43
+ "status": "disabled",
44
+ "remark": "updated"
45
+ }
46
+ ```
47
+
48
+ ## DELETE /<%= moduleName %>/delete
49
+
50
+ Query:
51
+
52
+ | Name | Required | Description |
53
+ |---|---|---|
54
+ | id | yes | Record ID |
55
+
56
+ ## Error cases
57
+
58
+ - `400`: missing ID, missing name, invalid status, or no update fields
59
+ - `404`: record not found
60
+ - `409`: name already exists
@@ -0,0 +1,103 @@
1
+ package <%= moduleName %>
2
+
3
+ import (
4
+ "strings"
5
+
6
+ "github.com/jom-io/gorig/utils/errors"
7
+ )
8
+
9
+ const (
10
+ StatusEnabled = "enabled"
11
+ StatusDisabled = "disabled"
12
+ )
13
+
14
+ type CreateReq struct {
15
+ Name string `json:"name" form:"name"`
16
+ Status string `json:"status" form:"status"`
17
+ Remark string `json:"remark" form:"remark"`
18
+ }
19
+
20
+ type UpdateReq struct {
21
+ ID int64 `json:"id" form:"id"`
22
+ Name string `json:"name" form:"name"`
23
+ Status string `json:"status" form:"status"`
24
+ Remark string `json:"remark" form:"remark"`
25
+ }
26
+
27
+ type QueryReq struct {
28
+ Name string `json:"name" form:"name"`
29
+ Status string `json:"status" form:"status"`
30
+ }
31
+
32
+ type Resp struct {
33
+ ID int64 `json:"id"`
34
+ Name string `json:"name"`
35
+ Status string `json:"status"`
36
+ Remark string `json:"remark"`
37
+ }
38
+
39
+ func normalizeStatus(status string) string {
40
+ status = strings.TrimSpace(status)
41
+ if status == "" {
42
+ return StatusEnabled
43
+ }
44
+ return status
45
+ }
46
+
47
+ func validStatus(status string) bool {
48
+ return status == StatusEnabled || status == StatusDisabled
49
+ }
50
+
51
+ func (r *CreateReq) Validate() *errors.Error {
52
+ if r == nil {
53
+ return errors.VerifyCode(400, "request is required")
54
+ }
55
+ r.Name = strings.TrimSpace(r.Name)
56
+ r.Status = normalizeStatus(r.Status)
57
+ r.Remark = strings.TrimSpace(r.Remark)
58
+ if r.Name == "" {
59
+ return errors.VerifyCode(400, "name is required")
60
+ }
61
+ if !validStatus(r.Status) {
62
+ return errors.VerifyCode(400, "status is invalid")
63
+ }
64
+ return nil
65
+ }
66
+
67
+ func (r *UpdateReq) Updates() (map[string]interface{}, *errors.Error) {
68
+ if r == nil {
69
+ return nil, errors.VerifyCode(400, "request is required")
70
+ }
71
+ if r.ID <= 0 {
72
+ return nil, errors.VerifyCode(400, "id is required")
73
+ }
74
+ updates := map[string]interface{}{}
75
+ if name := strings.TrimSpace(r.Name); name != "" {
76
+ updates["name"] = name
77
+ }
78
+ if status := strings.TrimSpace(r.Status); status != "" {
79
+ if !validStatus(status) {
80
+ return nil, errors.VerifyCode(400, "status is invalid")
81
+ }
82
+ updates["status"] = status
83
+ }
84
+ if remark := strings.TrimSpace(r.Remark); remark != "" {
85
+ updates["remark"] = remark
86
+ }
87
+ if len(updates) == 0 {
88
+ return nil, errors.VerifyCode(400, "no update fields")
89
+ }
90
+ return updates, nil
91
+ }
92
+
93
+ func (r *QueryReq) Normalize() *errors.Error {
94
+ if r == nil {
95
+ return nil
96
+ }
97
+ r.Name = strings.TrimSpace(r.Name)
98
+ r.Status = strings.TrimSpace(r.Status)
99
+ if r.Status != "" && !validStatus(r.Status) {
100
+ return errors.VerifyCode(400, "status is invalid")
101
+ }
102
+ return nil
103
+ }
@@ -0,0 +1,17 @@
1
+ //go:build integration && <%= db %>
2
+
3
+ package test
4
+
5
+ import "github.com/jom-io/gorig/domainx"
6
+
7
+ func init() {
8
+ <% if (db === 'mysql') { -%>
9
+ if err := domainx.GormDBServ.Start(); err != nil {
10
+ panic(err)
11
+ }
12
+ <% } else { -%>
13
+ if err := domainx.MongoDBServ.Start(); err != nil {
14
+ panic(err)
15
+ }
16
+ <% } -%>
17
+ }
@@ -0,0 +1,66 @@
1
+ //go:build integration && <%= db %>
2
+
3
+ package test
4
+
5
+ import (
6
+ "context"
7
+ "fmt"
8
+ "testing"
9
+ "time"
10
+
11
+ "<%= projectName %>/domain/<%= moduleName %>"
12
+
13
+ "github.com/jom-io/gorig/apix/load"
14
+ )
15
+
16
+ func Test<%= ModuleName %>IntegrationCRUD(t *testing.T) {
17
+ ctx := context.Background()
18
+ name := fmt.Sprintf("<%= moduleName %>-%d", time.Now().UnixNano())
19
+
20
+ id, err := <%= moduleName %>.Create(ctx, &<%= moduleName %>.CreateReq{
21
+ Name: name,
22
+ Status: <%= moduleName %>.StatusEnabled,
23
+ Remark: "integration",
24
+ })
25
+ if err != nil {
26
+ t.Fatalf("Create failed: %v", err)
27
+ }
28
+ t.Cleanup(func() {
29
+ _ = <%= moduleName %>.Delete(ctx, id)
30
+ })
31
+
32
+ info, err := <%= moduleName %>.Info(ctx, id)
33
+ if err != nil || info == nil || info.Name != name {
34
+ t.Fatalf("Info failed: info=%+v err=%v", info, err)
35
+ }
36
+
37
+ list, err := <%= moduleName %>.List(ctx, &<%= moduleName %>.QueryReq{Name: name})
38
+ if err != nil || len(list) != 1 || list[0].ID != id {
39
+ t.Fatalf("List failed: list=%+v err=%v", list, err)
40
+ }
41
+
42
+ page, err := <%= moduleName %>.Page(ctx, &<%= moduleName %>.QueryReq{Name: name}, load.BuildPage(ctx, 1, 10, 0))
43
+ if err != nil || page == nil {
44
+ t.Fatalf("Page failed: page=%+v err=%v", page, err)
45
+ }
46
+ pageItems, ok := page.Result.([]*<%= moduleName %>.Resp)
47
+ if !ok || len(pageItems) != 1 || pageItems[0].ID != id {
48
+ t.Fatalf("Page result failed: result=%+v", page.Result)
49
+ }
50
+
51
+ if err := <%= moduleName %>.Update(ctx, &<%= moduleName %>.UpdateReq{
52
+ ID: id,
53
+ Status: <%= moduleName %>.StatusDisabled,
54
+ }); err != nil {
55
+ t.Fatalf("Update failed: %v", err)
56
+ }
57
+
58
+ updated, err := <%= moduleName %>.Info(ctx, id)
59
+ if err != nil || updated == nil || updated.Status != <%= moduleName %>.StatusDisabled {
60
+ t.Fatalf("updated Info failed: info=%+v err=%v", updated, err)
61
+ }
62
+
63
+ if err := <%= moduleName %>.Delete(ctx, id); err != nil {
64
+ t.Fatalf("Delete failed: %v", err)
65
+ }
66
+ }
@@ -0,0 +1,25 @@
1
+ package model
2
+
3
+ import "github.com/jom-io/gorig/domainx"
4
+
5
+ const Table = "<%= moduleName %>"
6
+
7
+ type D struct {
8
+ <% if (db === 'mysql') { -%>
9
+ Name string `gorm:"column:name;type:varchar(128);not null" json:"name" form:"name"`
10
+ Status string `gorm:"column:status;type:varchar(32);not null" json:"status" form:"status"`
11
+ Remark string `gorm:"column:remark;type:varchar(512)" json:"remark" form:"remark"`
12
+ <% } else { -%>
13
+ Name string `bson:"name" json:"name" form:"name"`
14
+ Status string `bson:"status" json:"status" form:"status"`
15
+ Remark string `bson:"remark" json:"remark" form:"remark"`
16
+ <% } -%>
17
+ }
18
+
19
+ func (*D) DConfig() (domainx.ConType, string, string) {
20
+ <% if (db === 'mysql') { -%>
21
+ return domainx.Mysql, "<%= dbName %>", Table
22
+ <% } else { -%>
23
+ return domainx.Mongo, "<%= dbName %>", Table
24
+ <% } -%>
25
+ }
@@ -0,0 +1,70 @@
1
+ # <%= moduleName %> module
2
+
3
+ Persistent CRUD module generated for <%= dbTitle %>.
4
+
5
+ ## Storage
6
+
7
+ - Backend: <%= dbTitle %>
8
+ - Database: `<%= dbName %>`
9
+ - Table/collection: `<%= moduleName %>`
10
+ - Unique index: `name`
11
+ - Normal index: `status`
12
+
13
+ ## Configuration
14
+
15
+ The generator adds a non-secret `<% if (db === 'mysql') { %>Mysql.<%= dbName %><% } else { %>mongo.<%= dbName %><% } %>` connection skeleton to `_bin/local.yaml`, `_bin/dev.yaml`, `_bin/prod.yaml`, and `test/_bin/local.yaml`.
16
+
17
+ Before integration testing, replace the local placeholder connection values or set environment variables:
18
+
19
+ <% if (db === 'mysql') { -%>
20
+ - `GORIG_MYSQL_<%= dbEnvName %>_WRITE_HOST`
21
+ - `GORIG_MYSQL_<%= dbEnvName %>_WRITE_PORT`
22
+ - `GORIG_MYSQL_<%= dbEnvName %>_WRITE_DATABASE`
23
+ - `GORIG_MYSQL_<%= dbEnvName %>_WRITE_USER`
24
+ - `GORIG_MYSQL_<%= dbEnvName %>_WRITE_PASS`
25
+ <% } else { -%>
26
+ - `GORIG_MONGO_<%= dbEnvName %>_URI`
27
+ - `GORIG_MONGO_<%= dbEnvName %>_DB_NAME`
28
+ - `GORIG_MONGO_<%= dbEnvName %>_AUTH_SOURCE`
29
+ - `GORIG_MONGO_<%= dbEnvName %>_AUTH_USER`
30
+ - `GORIG_MONGO_<%= dbEnvName %>_AUTH_PASSWORD`
31
+ <% } -%>
32
+
33
+ Tests normally load `test/_bin/local.yaml` because the Go test package runs with `test/` as its working directory. Do not put real production credentials in committed YAML.
34
+
35
+ ## Service operations
36
+
37
+ - `Create(ctx, req)`
38
+ - `Info(ctx, id)`
39
+ - `List(ctx, req)`
40
+ - `Page(ctx, req, pageReq)`
41
+ - `Update(ctx, req)`
42
+ - `Delete(ctx, id)`
43
+
44
+ <% if (http) { -%>
45
+ ## HTTP routes
46
+
47
+ - POST `/<%= moduleName %>/create`
48
+ - GET `/<%= moduleName %>/info?id=`
49
+ - GET `/<%= moduleName %>/list`
50
+ - GET `/<%= moduleName %>/page?page=1&size=10`
51
+ - POST `/<%= moduleName %>/update`
52
+ - DELETE `/<%= moduleName %>/delete?id=`
53
+ <% } -%>
54
+
55
+ ## Verification
56
+
57
+ ```sh
58
+ go fmt ./...
59
+ go vet ./...
60
+ go build ./...
61
+ go test ./... -v
62
+ ```
63
+
64
+ Database integration requires a reachable <%= dbTitle %> connection for `<%= dbName %>`:
65
+
66
+ ```sh
67
+ go test -tags=integration,<%= db %> ./test/... -v
68
+ ```
69
+
70
+ The integration build tag keeps ordinary unit tests independent of external database availability.