From fda94c49656e561fcbbdbe3a862303ba304b3757 Mon Sep 17 00:00:00 2001 From: zhangwenjian Date: Sun, 27 Sep 2026 20:46:12 +0800 Subject: [PATCH] =?UTF-8?q?refactor=F0=9F=8E=A8:=20move=20app/jobs=20onto?= =?UTF-8?q?=20the=20generic=20actions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The five /sysjob routes use the generic actions. The detail route uses ViewAs to keep answering SysJobItem, whose entryId the edit form reads where the model marshals entry_id. GenerateM becomes ToModel, and the Generate methods go. Recorded before the change and replayed after it, the same 14 requests get byte-identical responses; answering the detail with the model instead of SysJobItem makes exactly the two detail requests differ. With a real ToModel on both modules, a test now builds a package that pairs the demo model with the jobs request and requires the build to fail on that pairing, next to the same package with the pair corrected. --- app/jobs/models/sys_job.go | 5 --- app/jobs/router/sys_job.go | 18 ++++------ app/jobs/service/dto/sys_job.go | 26 +------------- common/actions/generic_compile_test.go | 36 ++++++++++++++++++++ common/actions/testdata/matched/matched.go | 11 ++++++ common/actions/testdata/mismatch/mismatch.go | 12 +++++++ 6 files changed, 67 insertions(+), 41 deletions(-) create mode 100644 common/actions/generic_compile_test.go create mode 100644 common/actions/testdata/matched/matched.go create mode 100644 common/actions/testdata/mismatch/mismatch.go diff --git a/app/jobs/models/sys_job.go b/app/jobs/models/sys_job.go index a8cc6bf9..9107465a 100644 --- a/app/jobs/models/sys_job.go +++ b/app/jobs/models/sys_job.go @@ -27,11 +27,6 @@ func (*SysJob) TableName() string { return "sys_job" } -func (e *SysJob) Generate() models.ActiveRecord { - o := *e - return &o -} - func (e *SysJob) GetId() interface{} { return e.JobId } diff --git a/app/jobs/router/sys_job.go b/app/jobs/router/sys_job.go index 090be794..3c6d44d5 100644 --- a/app/jobs/router/sys_job.go +++ b/app/jobs/router/sys_job.go @@ -19,17 +19,13 @@ func registerSysJobRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddlew r := v1.Group("/sysjob").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) { - sysJob := &models2.SysJob{} - r.GET("", actions.PermissionAction(), actions.IndexAction(sysJob, new(dto2.SysJobSearch), func() interface{} { - list := make([]models2.SysJob, 0) - return &list - })) - r.GET("/:id", actions.PermissionAction(), actions.ViewAction(new(dto2.SysJobById), func() interface{} { - return &dto2.SysJobItem{} - })) - r.POST("", actions.CreateAction(new(dto2.SysJobControl))) - r.PUT("", actions.PermissionAction(), actions.UpdateAction(new(dto2.SysJobControl))) - r.DELETE("", actions.PermissionAction(), actions.DeleteAction(new(dto2.SysJobById))) + r.GET("", actions.PermissionAction(), actions.Index[models2.SysJob, dto2.SysJobSearch]()) + // The detail answers SysJobItem, whose entryId the edit form reads; + // the model itself marshals it as entry_id. + r.GET("/:id", actions.PermissionAction(), actions.ViewAs[models2.SysJob, dto2.SysJobById, dto2.SysJobItem]()) + r.POST("", actions.Create[models2.SysJob, dto2.SysJobControl]()) + r.PUT("", actions.PermissionAction(), actions.Update[models2.SysJob, dto2.SysJobControl]()) + r.DELETE("", actions.PermissionAction(), actions.Delete[models2.SysJob, dto2.SysJobById]()) } sysJob := apis.SysJob{} diff --git a/app/jobs/service/dto/sys_job.go b/app/jobs/service/dto/sys_job.go index f342f927..1ab9310f 100644 --- a/app/jobs/service/dto/sys_job.go +++ b/app/jobs/service/dto/sys_job.go @@ -6,7 +6,6 @@ import ( "go-admin/app/jobs/models" "go-admin/common/dto" - common "go-admin/common/models" ) type SysJobSearch struct { @@ -32,11 +31,6 @@ func (m *SysJobSearch) Bind(ctx *gin.Context) error { return err } -func (m *SysJobSearch) Generate() dto.Index { - o := *m - return &o -} - type SysJobControl struct { JobId int `json:"jobId"` JobName string `json:"jobName" validate:"required"` // 名称 @@ -55,12 +49,7 @@ func (s *SysJobControl) Bind(ctx *gin.Context) error { return ctx.ShouldBind(s) } -func (s *SysJobControl) Generate() dto.Control { - cp := *s - return &cp -} - -func (s *SysJobControl) GenerateM() (common.ActiveRecord, error) { +func (s *SysJobControl) ToModel() (*models.SysJob, error) { return &models.SysJob{ JobId: s.JobId, JobName: s.JobName, @@ -76,23 +65,10 @@ func (s *SysJobControl) GenerateM() (common.ActiveRecord, error) { }, nil } -func (s *SysJobControl) GetId() interface{} { - return s.JobId -} - type SysJobById struct { dto.ObjectById } -func (s *SysJobById) Generate() dto.Control { - cp := *s - return &cp -} - -func (s *SysJobById) GenerateM() (common.ActiveRecord, error) { - return &models.SysJob{}, nil -} - type SysJobItem struct { JobId int `json:"jobId"` JobName string `json:"jobName" validate:"required"` // 名称 diff --git a/common/actions/generic_compile_test.go b/common/actions/generic_compile_test.go new file mode 100644 index 00000000..d9838bc6 --- /dev/null +++ b/common/actions/generic_compile_test.go @@ -0,0 +1,36 @@ +package actions_test + +import ( + "os/exec" + "path/filepath" + "strings" + "testing" +) + +// A model paired with a request that builds a different model does not +// compile - the thing the older actions left to a type assertion at run +// time. Checked by building a package that does it, next to a control that +// is the same package with the pair corrected. +func TestGenericActionsRefuseAMismatchedPair(t *testing.T) { + root, err := filepath.Abs("../..") + if err != nil { + t.Fatal(err) + } + vet := func(pkg string) (string, error) { + cmd := exec.Command("go", "vet", "./common/actions/testdata/"+pkg) + cmd.Dir = root + out, err := cmd.CombinedOutput() + return string(out), err + } + + if out, err := vet("matched"); err != nil { + t.Fatalf("the control does not build, so the mismatch below proves nothing:\n%s", out) + } + out, err := vet("mismatch") + if err == nil { + t.Fatal("a model paired with another model's request compiled") + } + if !strings.Contains(out, "does not satisfy") || !strings.Contains(out, "ToModel") { + t.Fatalf("the build failed, but not on the pairing:\n%s", out) + } +} diff --git a/common/actions/testdata/matched/matched.go b/common/actions/testdata/matched/matched.go new file mode 100644 index 00000000..64d80265 --- /dev/null +++ b/common/actions/testdata/matched/matched.go @@ -0,0 +1,11 @@ +// Package matched is mismatch with the pair corrected: the control that +// shows mismatch fails for the pairing, not for how it is built. +package matched + +import ( + "go-admin/app/demo/models" + "go-admin/app/demo/service/dto" + "go-admin/common/actions" +) + +var _ = actions.Create[models.DemoProduct, dto.DemoProductControl] diff --git a/common/actions/testdata/mismatch/mismatch.go b/common/actions/testdata/mismatch/mismatch.go new file mode 100644 index 00000000..99eac73d --- /dev/null +++ b/common/actions/testdata/mismatch/mismatch.go @@ -0,0 +1,12 @@ +// Package mismatch pairs a model with another model's request, which must +// not compile. TestGenericActionsRefuseAMismatchedPair builds it and expects +// the build to fail; testdata keeps it out of ./... . +package mismatch + +import ( + "go-admin/app/demo/models" + jobdto "go-admin/app/jobs/service/dto" + "go-admin/common/actions" +) + +var _ = actions.Create[models.DemoProduct, jobdto.SysJobControl]