gorig-cli 1.0.23 → 1.0.25
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.
- package/README.md +112 -22
- package/commands/create.js +267 -112
- package/commands/doc.js +21 -8
- package/commands/init.js +316 -255
- package/commands/skill.js +55 -17
- package/package.json +1 -1
- package/templates/config.go.ejs +6 -5
- package/templates/config.yaml.ejs +3 -38
- package/templates/controller.go.ejs +11 -42
- package/templates/crud.controller.go.ejs +71 -0
- package/templates/crud.doc.md.ejs +60 -0
- package/templates/crud.dto.go.ejs +103 -0
- package/templates/crud.integration.init_test.go.ejs +17 -0
- package/templates/crud.integration_test.go.ejs +66 -0
- package/templates/crud.model.go.ejs +25 -0
- package/templates/crud.module.README.md.ejs +70 -0
- package/templates/crud.mongo.config.yaml.ejs +19 -0
- package/templates/crud.mysql.config.yaml.ejs +14 -0
- package/templates/crud.router.go.ejs +18 -0
- package/templates/crud.service.go.ejs +179 -0
- package/templates/crud.test.go.ejs +52 -0
- package/templates/dto.go.ejs +16 -0
- package/templates/gitignore.ejs +18 -49
- package/templates/hello.controller.go.ejs +19 -0
- package/templates/hello.router.go.ejs +12 -0
- package/templates/hello.service.go.ejs +31 -0
- package/templates/hello.test.go.ejs +34 -0
- package/templates/init.go.ejs +1 -27
- package/templates/main.go.ejs +2 -5
- package/templates/model.go.ejs +3 -4
- package/templates/project.README.md.ejs +35 -0
- package/templates/router.go.ejs +4 -8
- package/templates/service.go.ejs +25 -77
- package/templates/skills/gorig-backend/SKILL.md +89 -0
- package/templates/skills/gorig-backend/agents/openai.yaml +4 -0
- package/templates/skills/gorig-backend/assets/api-doc-template.md +59 -0
- package/templates/skills/gorig-backend/assets/gorig.gitignore +65 -0
- package/templates/skills/gorig-backend/assets/module-readme-template.md +58 -0
- package/templates/skills/gorig-backend/references/advanced-data-access.md +275 -0
- package/templates/skills/gorig-backend/references/auth-security.md +194 -0
- package/templates/skills/gorig-backend/references/business-scenarios.md +155 -0
- package/templates/skills/gorig-backend/references/cache.md +301 -0
- package/templates/skills/gorig-backend/references/capability-matrix.md +37 -0
- package/templates/skills/gorig-backend/references/configuration.md +48 -0
- package/templates/skills/gorig-backend/references/framework-api.md +190 -0
- package/templates/skills/gorig-backend/references/messaging.md +143 -0
- package/templates/skills/gorig-backend/references/onboarding-files.md +46 -0
- package/templates/skills/gorig-backend/references/outbound-http.md +162 -0
- package/templates/skills/gorig-backend/references/persistent-crud.md +332 -0
- package/templates/skills/gorig-backend/references/project-bootstrap.md +128 -0
- package/templates/skills/gorig-backend/references/scheduled-tasks.md +231 -0
- package/templates/skills/gorig-backend/references/service-lifecycle.md +51 -0
- package/templates/skills/gorig-backend/references/source-map.md +43 -0
- package/templates/skills/gorig-backend/references/source-policy.md +58 -0
- package/templates/skills/gorig-backend/references/sse.md +121 -0
- package/templates/skills/gorig-backend/references/testing.md +171 -0
- package/templates/skills/gorig-backend/scripts/check-source-links.sh +48 -0
- package/templates/skills/gorig-backend/scripts/detect-gorig-context.sh +67 -0
- package/templates/skills/gorig-backend/scripts/verify-basic-project.sh +108 -0
- package/npm_publish copy.sh +0 -65
- package/templates/internal.go.ejs +0 -49
- package/templates/req.go.ejs +0 -8
- package/templates/resp.go.ejs +0 -8
- package/templates/service.pub.go.ejs +0 -22
- package/templates/skills/claude/gorig-backend/SKILL.md +0 -189
- package/templates/skills/claude/gorig-backend/assets/api-doc-template.md +0 -50
- package/templates/skills/claude/gorig-backend/assets/module-readme-template.md +0 -46
- package/templates/skills/claude/gorig-backend/references/onboarding-files.md +0 -38
- package/templates/skills/codex/gorig-backend/SKILL.md +0 -189
- package/templates/skills/codex/gorig-backend/agents/openai.yaml +0 -4
- package/templates/skills/codex/gorig-backend/assets/api-doc-template.md +0 -50
- package/templates/skills/codex/gorig-backend/assets/module-readme-template.md +0 -46
- package/templates/skills/codex/gorig-backend/references/onboarding-files.md +0 -38
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
|
|
19
|
-
const
|
|
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
|
|
26
|
+
const codexBase = scope === 'project'
|
|
27
|
+
? path.join(cwd, '.agents')
|
|
28
|
+
: path.join(home, '.agents');
|
|
29
|
+
|
|
24
30
|
installs.push({
|
|
25
|
-
label:
|
|
26
|
-
source
|
|
27
|
-
destination: path.join(
|
|
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(
|
|
34
|
-
: path.join(
|
|
39
|
+
? path.join(cwd, '.claude')
|
|
40
|
+
: path.join(home, '.claude');
|
|
35
41
|
|
|
36
42
|
installs.push({
|
|
37
43
|
label: `Claude (${scope})`,
|
|
38
|
-
source
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
125
|
+
process.exitCode = 1;
|
|
126
|
+
return 1;
|
|
89
127
|
}
|
|
90
128
|
};
|
|
91
129
|
|
package/package.json
CHANGED
package/templates/config.go.ejs
CHANGED
|
@@ -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", "
|
|
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:
|
|
7
|
+
addr: ":<%= port %>"
|
|
8
8
|
|
|
9
|
-
#
|
|
10
|
-
#
|
|
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
|
|
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
|
-
|
|
23
|
-
|
|
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
|
-
|
|
34
|
-
|
|
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
|
|
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
|
-
|
|
55
|
-
|
|
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
|
-
|
|
61
|
-
|
|
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.
|