gorig-cli 1.0.24 → 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 -766
- 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 -44
- package/templates/skills/codex/gorig-backend/SKILL.md +0 -766
- 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 -44
package/templates/service.go.ejs
CHANGED
|
@@ -1,93 +1,41 @@
|
|
|
1
1
|
package <%= moduleName %>
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
"github.com/jom-io/gorig/httpx"
|
|
4
|
+
"context"
|
|
5
|
+
"strings"
|
|
6
|
+
|
|
8
7
|
"github.com/jom-io/gorig/utils/errors"
|
|
9
8
|
"github.com/jom-io/gorig/utils/logger"
|
|
10
9
|
"go.uber.org/zap"
|
|
11
|
-
"<%= projectName %>/domain/<%= moduleName %>/api/req"
|
|
12
|
-
"<%= projectName %>/domain/<%= moduleName %>/internal"
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
var gService = &service{}
|
|
16
|
-
|
|
17
|
-
type service struct {
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
func (s service) Save<%= ModuleName %>(ctx *gin.Context, req *req.<%= ModuleName %>Req) *errors.Error {
|
|
21
|
-
logger.Info(ctx, "Save<%= ModuleName %>", zap.Any("req", req))
|
|
22
|
-
<%= moduleName %>M := internal.<%= ModuleName %>()
|
|
23
|
-
if req.ID > 0 {
|
|
24
|
-
get<%= ModuleName %> := internal.<%= ModuleName %>()
|
|
25
|
-
if e := domainx.MustGetByID(get<%= ModuleName %>.Con, req.ID, get<%= ModuleName %>); e != nil {
|
|
26
|
-
return e
|
|
27
|
-
}
|
|
28
|
-
<%= moduleName %>M.ID = get<%= ModuleName %>.ID
|
|
29
|
-
<%= moduleName %>M.Options = get<%= ModuleName %>.Options
|
|
30
|
-
}
|
|
31
|
-
<%= moduleName %>M.ParseReq(req)
|
|
32
|
-
if _, e := domainx.Save(<%= moduleName %>M.Con, <%= moduleName %>M, 0); e != nil {
|
|
33
|
-
return e
|
|
34
|
-
}
|
|
35
|
-
return nil
|
|
36
|
-
}
|
|
37
10
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if filter == nil {
|
|
41
|
-
return nil, errors.Verify("参数错误")
|
|
42
|
-
}
|
|
43
|
-
query := domainx.Matches{}
|
|
44
|
-
query.Eq("user_id", httpx.GetUserIDInt64(ctx))
|
|
45
|
-
<%= moduleName %>Resp := new(load.PageResp)
|
|
46
|
-
<%= moduleName %> := internal.<%= ModuleName %>()
|
|
47
|
-
list := &[]internal.<%= ModuleName %>M{}
|
|
48
|
-
if e := domainx.FindByPageMatch(<%= moduleName %>.Con, query, pageReq, <%= moduleName %>Resp, list); e != nil {
|
|
49
|
-
return nil, e
|
|
50
|
-
}
|
|
51
|
-
<%= moduleName %>Resp.Result = <%= moduleName %>.ToRespList(list)
|
|
52
|
-
return <%= moduleName %>Resp, nil
|
|
53
|
-
}
|
|
11
|
+
"<%= projectName %>/domain/<%= moduleName %>/model"
|
|
12
|
+
)
|
|
54
13
|
|
|
55
|
-
func (
|
|
56
|
-
|
|
57
|
-
if
|
|
58
|
-
return nil, errors.Verify("
|
|
14
|
+
func BuildInfo(ctx context.Context, name string) (*InfoResp, *errors.Error) {
|
|
15
|
+
name = strings.TrimSpace(name)
|
|
16
|
+
if name == "" {
|
|
17
|
+
return nil, errors.Verify("name cannot be empty")
|
|
59
18
|
}
|
|
60
|
-
query := domainx.Matches{}
|
|
61
|
-
query.Eq("user_id", httpx.GetUserIDInt64(ctx))
|
|
62
|
-
<%= moduleName %> := internal.<%= ModuleName %>()
|
|
63
|
-
list := &[]internal.<%= ModuleName %>M{}
|
|
64
|
-
if e := domainx.FindByMatch(<%= moduleName %>.Con, query, list); e != nil {
|
|
65
|
-
return nil, e
|
|
66
|
-
}
|
|
67
|
-
return <%= moduleName %>.ToRespList(list), nil
|
|
68
|
-
}
|
|
69
19
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
20
|
+
logger.Info(ctx, "building <%= moduleName %> info", zap.String("name", name))
|
|
21
|
+
return &InfoResp{
|
|
22
|
+
Item: model.D{
|
|
23
|
+
ID: "<%= moduleName %>",
|
|
24
|
+
Name: name,
|
|
25
|
+
},
|
|
26
|
+
Message: "hello " + name,
|
|
27
|
+
}, nil
|
|
77
28
|
}
|
|
78
29
|
|
|
79
|
-
func (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if e := domainx.MustGetByID(<%= moduleName %>M.Con, id, <%= moduleName %>M); e != nil {
|
|
83
|
-
return e
|
|
30
|
+
func EchoMessage(ctx context.Context, req *EchoReq) (*EchoResp, *errors.Error) {
|
|
31
|
+
if req == nil {
|
|
32
|
+
return nil, errors.Verify("request cannot be nil")
|
|
84
33
|
}
|
|
85
|
-
|
|
86
|
-
|
|
34
|
+
message := strings.TrimSpace(req.Message)
|
|
35
|
+
if message == "" {
|
|
36
|
+
return nil, errors.Verify("message cannot be empty")
|
|
87
37
|
}
|
|
88
|
-
return nil
|
|
89
|
-
}
|
|
90
38
|
|
|
91
|
-
|
|
92
|
-
|
|
39
|
+
logger.Info(ctx, "echo <%= moduleName %> message", zap.String("message", message))
|
|
40
|
+
return &EchoResp{Message: message}, nil
|
|
93
41
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gorig-backend
|
|
3
|
+
description: Build, extend, review, test, troubleshoot, and deploy Go backend projects based on github.com/jom-io/gorig. Use for Gorig project initialization, environment configuration, Router-Controller-Service-Model modules, apix/httpx/serv/bootstrap usage, MySQL or MongoDB through domainx/dx, cache, cronx, messagex, SSE, token authentication, middleware, observability, deployment, and Gorig ecosystem integration. Resolve the target project's actual Gorig version before selecting APIs.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Gorig Backend
|
|
7
|
+
|
|
8
|
+
Develop Gorig projects from verified source behavior. Treat static examples as fallbacks, never as stronger evidence than the target project's dependency source.
|
|
9
|
+
|
|
10
|
+
## Start Every Task
|
|
11
|
+
|
|
12
|
+
1. Read `references/source-policy.md`.
|
|
13
|
+
2. Read `references/onboarding-files.md` and inspect the target repository.
|
|
14
|
+
3. Run `scripts/detect-gorig-context.sh [project-root]` when a shell is available.
|
|
15
|
+
4. Read `references/capability-matrix.md` before selecting a component.
|
|
16
|
+
5. If the user describes a business outcome rather than naming a precise framework component, read `references/business-scenarios.md`.
|
|
17
|
+
6. Load only the references required by the request.
|
|
18
|
+
|
|
19
|
+
If the project has no `go.mod`, treat the task as new-project design. Do not assume the latest Gorig API is compatible with an existing project.
|
|
20
|
+
|
|
21
|
+
## Route the Request
|
|
22
|
+
|
|
23
|
+
- Business feature requests in ordinary language, workflow automation, reminders, notifications, real-time UI updates, delayed processing, or performance complaints: read `references/business-scenarios.md` first, then route to the component references selected by the scenario analysis.
|
|
24
|
+
- New runnable project or project initialization: read `references/project-bootstrap.md`, `references/configuration.md`, `references/service-lifecycle.md`, and `references/testing.md`.
|
|
25
|
+
- Environment selection or configuration: read `references/configuration.md`.
|
|
26
|
+
- Startup, shutdown, built-in services, or custom services: read `references/service-lifecycle.md`.
|
|
27
|
+
- Persistent CRUD modules or MySQL/Mongo-backed business modules: read `references/persistent-crud.md`, `references/framework-api.md`, and `references/testing.md`. Add HTTP routes/controllers only when HTTP is in scope.
|
|
28
|
+
- Advanced data access, aggregate reads, batch scans, indexes, direct-driver escape hatches, or transaction boundaries: read `references/advanced-data-access.md`, `references/framework-api.md`, and `references/testing.md`.
|
|
29
|
+
- Cache, cache-aside, local cache persistence, Redis cache, counters, invalidation, or multi-level caching: read `references/cache.md`, `references/framework-api.md`, and `references/testing.md`.
|
|
30
|
+
- Framework registration, non-CRUD HTTP APIs, logging, or generic middleware: read `references/framework-api.md`.
|
|
31
|
+
- Authentication, login/logout, token refresh/revocation, protected routes, user context, roles, forbidden responses, CORS, debounce/rate protection, request signing, secrets, or sensitive logs: read `references/auth-security.md`, `references/framework-api.md`, and `references/testing.md`.
|
|
32
|
+
- Outbound HTTP calls, external API integration, GET/form/JSON/XML requests, forwarded headers, context propagation, timeouts, bad upstream responses, or image fetching: read `references/outbound-http.md`, `references/framework-api.md`, and `references/testing.md`.
|
|
33
|
+
- Scheduled tasks, cron expressions, delayed jobs, one-shot jobs, timeout, panic recovery, deduplication, or cron shutdown: read `references/scheduled-tasks.md`, `references/framework-api.md`, and `references/testing.md`.
|
|
34
|
+
- Pub/sub messaging, local or Redis broker, sequential subscribers, retry, DLQ, replay, unsubscribe, or message-to-SSE composition: read `references/messaging.md`, `references/framework-api.md`, and `references/testing.md`.
|
|
35
|
+
- Server-Sent Events, streaming routes, event/error payloads, disconnect handling, or SSE composition: read `references/sse.md`, `references/framework-api.md`, and `references/testing.md`.
|
|
36
|
+
- Tests, smoke checks, or delivery validation: read `references/testing.md`.
|
|
37
|
+
- Missing local source, upstream comparison, latest behavior, or ecosystem repositories: read `references/source-map.md`.
|
|
38
|
+
- API documentation changes: use `assets/api-doc-template.md`.
|
|
39
|
+
- Module behavior changes: use `assets/module-readme-template.md`.
|
|
40
|
+
|
|
41
|
+
Detailed scenario references will be added phase by phase. Until a scenario has a dedicated verified reference, inspect the resolved framework source and its tests before implementation.
|
|
42
|
+
|
|
43
|
+
## Use the Conversation as a Development Workflow
|
|
44
|
+
|
|
45
|
+
For a new project or a material architecture, API, persistence, security, runtime behavior, configuration, external dependency, or deployment change:
|
|
46
|
+
|
|
47
|
+
1. Restate the business goal, boundaries, and assumptions.
|
|
48
|
+
2. If the request is business-language, decompose it into trigger, durability, audience, data, and operational needs before choosing components.
|
|
49
|
+
3. Identify the Gorig and Go versions plus external dependencies.
|
|
50
|
+
4. Present a recommended implementation in business language; list alternatives only when there is a real tradeoff.
|
|
51
|
+
5. Separate minimum viable implementation from optional future expansion.
|
|
52
|
+
6. Present the route, data, configuration, file, test, and deployment impact.
|
|
53
|
+
7. Wait for explicit confirmation before the first material edit.
|
|
54
|
+
8. Implement one runnable vertical slice at a time.
|
|
55
|
+
9. Verify the business effect of each slice before adding the next component.
|
|
56
|
+
|
|
57
|
+
For an explicitly requested narrow fix with no material design choice, inspect and implement directly.
|
|
58
|
+
|
|
59
|
+
## Apply Core Framework Rules
|
|
60
|
+
|
|
61
|
+
- Start applications with `bootstrap.StartUp()` unless the resolved version requires a different entry point.
|
|
62
|
+
- Register custom lifecycle services with `serv.RegisterService`.
|
|
63
|
+
- Register HTTP routes with `httpx.RegisterRouter` and the callback type used by the resolved source.
|
|
64
|
+
- Add `defer apix.HandlePanic(ctx)` to controllers when consistent with the target project.
|
|
65
|
+
- Bind and validate input with the resolved `apix` signatures.
|
|
66
|
+
- Return the established response envelope and business error code.
|
|
67
|
+
- Keep transport logic in controllers and business logic in services.
|
|
68
|
+
- Propagate request context through services, data access, logs, and outbound calls.
|
|
69
|
+
- Use `utils/errors` and structured `utils/logger` calls.
|
|
70
|
+
- Prefer `domainx/dx` for supported data operations; use direct drivers only when the abstraction lacks the required behavior.
|
|
71
|
+
- Push filtering, sorting, pagination, counting, and aggregation into the database or verified framework APIs. Do not load all records into memory and then filter, sort, or paginate in Go.
|
|
72
|
+
- Register migrations and indexes according to the resolved framework implementation.
|
|
73
|
+
- Never advertise `experimental` or `unsupported` capability as production-ready.
|
|
74
|
+
|
|
75
|
+
## Verify Before Delivery
|
|
76
|
+
|
|
77
|
+
Use the target repository's commands when documented. Otherwise run the applicable checks from `references/testing.md`.
|
|
78
|
+
|
|
79
|
+
At minimum:
|
|
80
|
+
|
|
81
|
+
- Format changed Go code.
|
|
82
|
+
- Build the affected packages.
|
|
83
|
+
- Run relevant tests, including failure paths.
|
|
84
|
+
- Exercise the delivered behavior with a local smoke check.
|
|
85
|
+
- Report exact commands and results.
|
|
86
|
+
- Report skipped checks and concrete blockers.
|
|
87
|
+
- Update API or module documentation when behavior changed.
|
|
88
|
+
|
|
89
|
+
Deliver a concise summary containing changed files, effect verification, compatibility assumptions, known limitations, and safe next steps.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "Gorig Backend"
|
|
3
|
+
short_description: "Version-aware Gorig backend development and deployment"
|
|
4
|
+
default_prompt: "Use $gorig-backend to inspect the target project's Gorig version, plan the requested backend change, implement it with verified framework APIs, and validate the resulting behavior."
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# <module> API Documentation
|
|
2
|
+
|
|
3
|
+
## 1. Module Overview
|
|
4
|
+
|
|
5
|
+
- Module name:
|
|
6
|
+
- Business objective:
|
|
7
|
+
- Dependent services/storage:
|
|
8
|
+
- Gorig version:
|
|
9
|
+
|
|
10
|
+
## 2. API List
|
|
11
|
+
|
|
12
|
+
| Name | Method | Path | Auth | Description |
|
|
13
|
+
|---|---|---|---|---|
|
|
14
|
+
| | | | | |
|
|
15
|
+
|
|
16
|
+
## 3. API Details
|
|
17
|
+
|
|
18
|
+
### 3.1 <api name>
|
|
19
|
+
|
|
20
|
+
- Method and path:
|
|
21
|
+
- Controller:
|
|
22
|
+
- Service:
|
|
23
|
+
- Description:
|
|
24
|
+
|
|
25
|
+
#### Request Parameters
|
|
26
|
+
|
|
27
|
+
| Field | Type | Required | Description |
|
|
28
|
+
|---|---|---|---|
|
|
29
|
+
| | | | |
|
|
30
|
+
|
|
31
|
+
#### Response Example
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"code": 0,
|
|
36
|
+
"msg": "ok",
|
|
37
|
+
"data": {}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
#### Error Codes
|
|
42
|
+
|
|
43
|
+
| code | Meaning | Trigger |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| | | |
|
|
46
|
+
|
|
47
|
+
## 4. Invocation and Verification
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
curl -X POST 'http://<host>/<path>' \
|
|
51
|
+
-H 'Content-Type: application/json' \
|
|
52
|
+
-d '{}'
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 5. Change Log
|
|
56
|
+
|
|
57
|
+
| Date | Author | Description |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| | | |
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Gorig runtime state
|
|
2
|
+
.cache/
|
|
3
|
+
.logs/
|
|
4
|
+
logs/
|
|
5
|
+
tmp/
|
|
6
|
+
temp/
|
|
7
|
+
.deploy/
|
|
8
|
+
tokens.json
|
|
9
|
+
*.log
|
|
10
|
+
*.jsonl
|
|
11
|
+
|
|
12
|
+
# Build and test output
|
|
13
|
+
bin/
|
|
14
|
+
dist/
|
|
15
|
+
build/
|
|
16
|
+
*.exe
|
|
17
|
+
*.exe~
|
|
18
|
+
*.dll
|
|
19
|
+
*.so
|
|
20
|
+
*.dylib
|
|
21
|
+
*.test
|
|
22
|
+
*.out
|
|
23
|
+
coverage.out
|
|
24
|
+
coverage.html
|
|
25
|
+
*.prof
|
|
26
|
+
cpu.out
|
|
27
|
+
mem.out
|
|
28
|
+
trace.out
|
|
29
|
+
|
|
30
|
+
# Local secrets and machine-specific overrides
|
|
31
|
+
.env
|
|
32
|
+
.env.*
|
|
33
|
+
!.env.example
|
|
34
|
+
*.pem
|
|
35
|
+
*.crt
|
|
36
|
+
*.key
|
|
37
|
+
*.p12
|
|
38
|
+
*.pfx
|
|
39
|
+
_bin/*secret*.yaml
|
|
40
|
+
_bin/*private*.yaml
|
|
41
|
+
_bin/*override*.yaml
|
|
42
|
+
_bin/*.local.yaml
|
|
43
|
+
|
|
44
|
+
# Go local workspace/cache
|
|
45
|
+
go.work
|
|
46
|
+
go.work.sum
|
|
47
|
+
.gocache/
|
|
48
|
+
|
|
49
|
+
# Editors and operating systems
|
|
50
|
+
.vscode/
|
|
51
|
+
.idea/
|
|
52
|
+
*.swp
|
|
53
|
+
*.swo
|
|
54
|
+
*.swn
|
|
55
|
+
*~
|
|
56
|
+
.DS_Store
|
|
57
|
+
Thumbs.db
|
|
58
|
+
|
|
59
|
+
# Optional local data dumps
|
|
60
|
+
data_smoke/
|
|
61
|
+
test/output/
|
|
62
|
+
*.sqlite
|
|
63
|
+
*.sqlite3
|
|
64
|
+
*.db
|
|
65
|
+
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# <module> Module README
|
|
2
|
+
|
|
3
|
+
## 1. Module Responsibility
|
|
4
|
+
|
|
5
|
+
- Objective:
|
|
6
|
+
- Boundary:
|
|
7
|
+
- Non-goals:
|
|
8
|
+
- Gorig version:
|
|
9
|
+
|
|
10
|
+
## 2. Directory Layout
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
domain/<module>/
|
|
14
|
+
├── router.go
|
|
15
|
+
├── controller.go
|
|
16
|
+
├── service.go
|
|
17
|
+
├── dto.go
|
|
18
|
+
└── model/
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 3. Request Flow
|
|
22
|
+
|
|
23
|
+
1. Router registers endpoints and middleware.
|
|
24
|
+
2. Controller handles parameters, panic handling, and responses.
|
|
25
|
+
3. Service executes business logic and error handling.
|
|
26
|
+
4. Model or dx performs data access.
|
|
27
|
+
|
|
28
|
+
## 4. Data Model
|
|
29
|
+
|
|
30
|
+
| Struct | Description | Key fields |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| | | |
|
|
33
|
+
|
|
34
|
+
## 5. External APIs
|
|
35
|
+
|
|
36
|
+
| Name | Method | Path | Description |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| | | | |
|
|
39
|
+
|
|
40
|
+
## 6. Configuration, Errors, and Logs
|
|
41
|
+
|
|
42
|
+
- Configuration keys:
|
|
43
|
+
- Error definition location:
|
|
44
|
+
- Common error codes:
|
|
45
|
+
- Key log fields:
|
|
46
|
+
|
|
47
|
+
## 7. Tests and Effect Verification
|
|
48
|
+
|
|
49
|
+
- Unit tests:
|
|
50
|
+
- Integration tests:
|
|
51
|
+
- Smoke test:
|
|
52
|
+
- Known limitations:
|
|
53
|
+
|
|
54
|
+
## 8. Maintenance Conventions
|
|
55
|
+
|
|
56
|
+
- Naming:
|
|
57
|
+
- Compatibility:
|
|
58
|
+
- Documentation update rules:
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# Advanced Data Access
|
|
2
|
+
|
|
3
|
+
Use this reference when a task goes beyond baseline CRUD and needs complex filters, aggregate reads, projections, batch scans, indexes, direct database access, or transaction boundaries.
|
|
4
|
+
|
|
5
|
+
Always inspect the target project's resolved Gorig source first. The examples below reflect the locally inspected Gorig `master` commit `35bbefb`.
|
|
6
|
+
|
|
7
|
+
## Preferred API
|
|
8
|
+
|
|
9
|
+
Prefer `github.com/jom-io/gorig/domainx/dx` for supported MySQL and MongoDB access.
|
|
10
|
+
|
|
11
|
+
Use legacy `domainx` package functions only when:
|
|
12
|
+
|
|
13
|
+
- the target project already uses them consistently,
|
|
14
|
+
- the needed behavior is not exposed by `dx`, or
|
|
15
|
+
- version inspection proves the target's `dx` facade lacks the method.
|
|
16
|
+
|
|
17
|
+
When using legacy calls, keep the same guards as `dx`: update and delete operations must have a non-zero ID or a non-empty match list.
|
|
18
|
+
|
|
19
|
+
## Optional Filter Semantics
|
|
20
|
+
|
|
21
|
+
`Eq`, `Like`, `Gt`, `In`, and related methods accept optional `ignore ...bool`.
|
|
22
|
+
|
|
23
|
+
In the inspected version, `ignore=true` means "ignore the empty-value check", so the zero or empty value is forced into the condition. For ordinary optional request filters, omit the boolean.
|
|
24
|
+
|
|
25
|
+
```go
|
|
26
|
+
q := dx.On[model.D](ctx).
|
|
27
|
+
Like("name", req.Name). // empty req.Name is skipped by the framework
|
|
28
|
+
Eq("status", req.Status). // empty req.Status is skipped by the framework
|
|
29
|
+
Sort("id")
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Use `true` only when zero is an intentional filter value:
|
|
33
|
+
|
|
34
|
+
```go
|
|
35
|
+
q := dx.On[model.D](ctx).Eq("retry_count", 0, true)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Complex Filters
|
|
39
|
+
|
|
40
|
+
Common comparison and set operations:
|
|
41
|
+
|
|
42
|
+
```go
|
|
43
|
+
q := dx.On[model.D](ctx).
|
|
44
|
+
Eq("tenant_id", tenantID).
|
|
45
|
+
Ne("status", "deleted").
|
|
46
|
+
Gte("created_at", startAt).
|
|
47
|
+
Lt("created_at", endAt).
|
|
48
|
+
Like("name", keyword).
|
|
49
|
+
In("type", []string{"standard", "premium"}).
|
|
50
|
+
NotIn("status", []string{"archived", "blocked"}).
|
|
51
|
+
NEmpty("external_id")
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Array field operations:
|
|
55
|
+
|
|
56
|
+
```go
|
|
57
|
+
q := dx.On[model.D](ctx).
|
|
58
|
+
Has("tags", "hot").
|
|
59
|
+
HasAny("tags", []string{"vip", "trial"}).
|
|
60
|
+
HasAll("flags", []string{"paid", "verified"})
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
MongoDB geo-spatial helpers:
|
|
64
|
+
|
|
65
|
+
```go
|
|
66
|
+
nearByLatLng := dx.On[model.D](ctx).Near("lat", "lng", lat, lng, 3000)
|
|
67
|
+
nearByLocation := dx.On[model.D](ctx).NearLoc("location", lat, lng, 3000)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Verify indexes and backend behavior before using geo queries in production. MySQL and MongoDB differ in supported geo field shapes and index requirements.
|
|
71
|
+
|
|
72
|
+
## Dynamic Conditions
|
|
73
|
+
|
|
74
|
+
When building conditions from loops or composable policy blocks, use `domainx.NewMatches()` and `AddMatches`.
|
|
75
|
+
|
|
76
|
+
```go
|
|
77
|
+
matches := domainx.NewMatches().
|
|
78
|
+
Eq("tenant_id", tenantID).
|
|
79
|
+
Eq("status", req.Status).
|
|
80
|
+
Gte("score", req.MinScore, req.MinScore == 0)
|
|
81
|
+
|
|
82
|
+
for _, tag := range req.Tags {
|
|
83
|
+
matches.Has("tags", tag)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
list, err := dx.On[model.D](ctx).
|
|
87
|
+
AddMatches(matches).
|
|
88
|
+
Sort("id").
|
|
89
|
+
Find()
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Be careful with `ignore=true` in dynamic builders. The example above forces `MinScore=0` only if the request explicitly treats zero as a valid minimum. Omit the third argument when an empty or zero request field should be skipped.
|
|
93
|
+
|
|
94
|
+
## Sorting and Projection
|
|
95
|
+
|
|
96
|
+
```go
|
|
97
|
+
items, err := dx.On[model.D](ctx).
|
|
98
|
+
Eq("tenant_id", tenantID).
|
|
99
|
+
Select("id", "name", "status", "created_at").
|
|
100
|
+
Omit("secret_note").
|
|
101
|
+
Sort("created_at", true). // true means ascending
|
|
102
|
+
Find()
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`Sort("field")` is descending by default. Projection is useful for large records, but verify backend-specific field names:
|
|
106
|
+
|
|
107
|
+
- MySQL fields normally match `gorm:"column:<name>"`.
|
|
108
|
+
- MongoDB fields normally match `bson:"<name>"`.
|
|
109
|
+
- Do not project sensitive fields back into API responses just because storage returns them.
|
|
110
|
+
|
|
111
|
+
## Reads, Aggregates, and Existence Checks
|
|
112
|
+
|
|
113
|
+
```go
|
|
114
|
+
one, err := dx.On[model.D](ctx).WithID(id).Get()
|
|
115
|
+
exists, err := dx.On[model.D](ctx).Eq("code", code).Exists()
|
|
116
|
+
count, err := dx.On[model.D](ctx).Eq("status", "active").Count()
|
|
117
|
+
sum, err := dx.On[model.D](ctx).Eq("tenant_id", tenantID).Sum("amount")
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Use `Get` only when a single record is expected. Use `Exists` for uniqueness checks instead of fetching full records when the value is not needed.
|
|
121
|
+
|
|
122
|
+
## Memory-Safe Query Processing
|
|
123
|
+
|
|
124
|
+
Query work must be pushed down to the database or handled through bounded framework iteration.
|
|
125
|
+
|
|
126
|
+
Do not:
|
|
127
|
+
|
|
128
|
+
- fetch all rows with `Find()` and then filter in Go;
|
|
129
|
+
- fetch all rows with `Find()` and then sort in Go for an API response;
|
|
130
|
+
- fetch all rows with `Find()` and then slice a page in Go;
|
|
131
|
+
- fetch all rows to count, sum, or check existence;
|
|
132
|
+
- scan an entire table in a background task without a restrictive match or operational reason.
|
|
133
|
+
|
|
134
|
+
Use:
|
|
135
|
+
|
|
136
|
+
- `Eq`, `Like`, `Gt/Gte/Lt/Lte`, `In`, `AddMatches`, and backend-specific filters before reading;
|
|
137
|
+
- `Sort` before `Find` or `Page`;
|
|
138
|
+
- `Page(page, size, lastID)` for API pagination;
|
|
139
|
+
- `Select` or `Omit` for large records;
|
|
140
|
+
- `Count`, `Sum`, and `Exists` for aggregate/existence checks;
|
|
141
|
+
- `FindEach` or `AllEach` for bounded batch processing when a job must touch many records.
|
|
142
|
+
|
|
143
|
+
If `dx` cannot express the needed query, use a direct driver with database-side `WHERE`/filter, `ORDER BY`, `LIMIT`, cursor, aggregation pipeline, or equivalent backend mechanism. The direct-driver path must include the same tenant/scope/time-range guards that a `dx` query would have.
|
|
144
|
+
|
|
145
|
+
In-memory processing is acceptable only after the database has already applied the main filter and bound the result size.
|
|
146
|
+
|
|
147
|
+
## Pagination and Batch Scans
|
|
148
|
+
|
|
149
|
+
Cursor-aware paging:
|
|
150
|
+
|
|
151
|
+
```go
|
|
152
|
+
resp, err := dx.On[model.D](ctx).
|
|
153
|
+
Eq("tenant_id", tenantID).
|
|
154
|
+
Sort("id").
|
|
155
|
+
Page(req.Page, req.Size, req.LastID)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Safe full scans:
|
|
159
|
+
|
|
160
|
+
```go
|
|
161
|
+
err := dx.On[model.D](ctx).
|
|
162
|
+
Eq("tenant_id", tenantID).
|
|
163
|
+
AllEach(func(item *domainx.Complex[model.D]) *errors.Error {
|
|
164
|
+
// Process one item. Keep the handler idempotent.
|
|
165
|
+
return nil
|
|
166
|
+
})
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
`AllEach` pages by ID in batches of 1000 in the inspected implementation. Always include a restrictive match for tenant, scope, or job boundary. Do not use it as an unbounded production table scan without an operational reason.
|
|
170
|
+
|
|
171
|
+
## Writes and Guards
|
|
172
|
+
|
|
173
|
+
```go
|
|
174
|
+
id, err := dx.On[model.D](ctx, &d).Save()
|
|
175
|
+
|
|
176
|
+
err = dx.On[model.D](ctx).WithID(id).Updates(map[string]interface{}{
|
|
177
|
+
"status": "done",
|
|
178
|
+
"updated_by": actorID,
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
err = dx.On[model.D](ctx).
|
|
182
|
+
Eq("tenant_id", tenantID).
|
|
183
|
+
Eq("expired", true, true).
|
|
184
|
+
Delete()
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
`Update`, `Updates`, `Delete`, `Get`, `Find`, `FindEach`, and `AllEach` guard against missing IDs or empty match lists in the inspected `dx` implementation. Preserve this behavior. Do not bypass it with direct drivers unless the replacement has an equal or stronger safety condition.
|
|
188
|
+
|
|
189
|
+
## Migrations and Indexes
|
|
190
|
+
|
|
191
|
+
Register indexes from `service.go` using `domainx.AutoMigrate`.
|
|
192
|
+
|
|
193
|
+
```go
|
|
194
|
+
func init() {
|
|
195
|
+
domainx.AutoMigrate(
|
|
196
|
+
func() domainx.ConTable {
|
|
197
|
+
return dx.On[model.D](context.Background()).Complex()
|
|
198
|
+
},
|
|
199
|
+
domainx.CtIdx(domainx.Unique, "tenant_id", "code"),
|
|
200
|
+
domainx.CtIdx(domainx.Idx, "tenant_id", "status", "created_at"),
|
|
201
|
+
domainx.CtIdx(domainx.Spatial2D, "lat", "lng"),
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Pass bare storage field names. Verify generated indexes against the selected backend:
|
|
207
|
+
|
|
208
|
+
- MySQL uses GORM migration plus explicit index creation in the inspected source.
|
|
209
|
+
- MongoDB maps fields through its own query builder and index implementation.
|
|
210
|
+
- Compound and unique index names must be stable enough for repeated migration runs.
|
|
211
|
+
- Geo-spatial indexes are backend-specific and must be proven with integration tests.
|
|
212
|
+
|
|
213
|
+
## Direct Driver Escape Hatches
|
|
214
|
+
|
|
215
|
+
Use direct database handles only when `dx` cannot express the required behavior, such as a backend-specific lock, transaction, aggregation pipeline, or raw SQL optimization.
|
|
216
|
+
|
|
217
|
+
Available inspected escape hatches:
|
|
218
|
+
|
|
219
|
+
```go
|
|
220
|
+
con := dx.On[model.D](ctx).Complex().GetCon()
|
|
221
|
+
raw, err := con.MustGetDB()
|
|
222
|
+
if err != nil {
|
|
223
|
+
return err
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
For MySQL, `raw` is expected to be `*gorm.DB`. For MongoDB, `raw` is expected to be `*qmgo.Client`.
|
|
228
|
+
|
|
229
|
+
Project-level connection helpers also exist:
|
|
230
|
+
|
|
231
|
+
```go
|
|
232
|
+
mysqlDB := domainx.UseDbConn("Main")
|
|
233
|
+
mongoClient := domainx.UseMongoDbConn("main")
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Rules for escape hatches:
|
|
237
|
+
|
|
238
|
+
- Keep direct-driver code inside the service or a small repository helper.
|
|
239
|
+
- Require explicit type assertions and nil checks.
|
|
240
|
+
- Keep tenant, ID, and match guards visible next to raw queries.
|
|
241
|
+
- Add integration tests for the exact backend path.
|
|
242
|
+
- Do not hide raw SQL or Mongo pipelines behind generic helper names.
|
|
243
|
+
|
|
244
|
+
## Transactions
|
|
245
|
+
|
|
246
|
+
The inspected `dx` facade does not expose a generic transaction wrapper. For MySQL-only work, use the direct `*gorm.DB` transaction API after verifying the target version:
|
|
247
|
+
|
|
248
|
+
```go
|
|
249
|
+
raw, err := dx.On[model.D](ctx).Complex().GetCon().MustGetDB()
|
|
250
|
+
if err != nil {
|
|
251
|
+
return err
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
db, ok := raw.(*gorm.DB)
|
|
255
|
+
if !ok || db == nil {
|
|
256
|
+
return errors.Sys("mysql connection is not available")
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if txErr := db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
260
|
+
return tx.Table(model.Table).Where("id = ?", id).Update("status", "done").Error
|
|
261
|
+
}); txErr != nil {
|
|
262
|
+
return errors.Sys(txErr.Error())
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
MongoDB transaction behavior is not covered by the current verified skill baseline. Inspect the resolved `qmgo` version and run a MongoDB integration test before advertising Mongo transaction support.
|
|
267
|
+
|
|
268
|
+
## Verification Checklist
|
|
269
|
+
|
|
270
|
+
- Compile the affected package against the resolved Gorig version.
|
|
271
|
+
- Run ordinary tests without external services.
|
|
272
|
+
- Run tagged or container-backed integration tests for MySQL and MongoDB paths.
|
|
273
|
+
- Verify optional filters with empty values and intentional zero values.
|
|
274
|
+
- Verify sorting, projection, pagination, count/sum, update/delete guards, and index creation.
|
|
275
|
+
- Record unsupported backend behavior and skipped checks explicitly.
|