diff --git a/app/system/internal/data/README.md b/app/system/internal/data/README.md new file mode 100644 index 0000000..711c884 --- /dev/null +++ b/app/system/internal/data/README.md @@ -0,0 +1,12 @@ +# Data Layer + +`data` owns database clients, persistence models, migrations, configuration +watching, and repository implementations. + +- `repository/`: system repositories and table persistence +- `payment/`: payment configuration and payment-order persistence +- root files: shared database lifecycle, runtime clients, integration-config + storage, data-scope auditing, and migration orchestration + +Root files intentionally stay in one package because they share `Data` state and +reload locks. Do not split them into packages only to reduce file count. diff --git a/app/system/internal/data/repository/task.go b/app/system/internal/data/repository/task.go index 5a426c9..6b013f7 100644 --- a/app/system/internal/data/repository/task.go +++ b/app/system/internal/data/repository/task.go @@ -85,6 +85,12 @@ func (r *taskRepo) FindTask(ctx context.Context, id uint) (*biz.TimedTask, error return taskFromPO(po), nil } func (r *taskRepo) ListTasks(ctx context.Context, page, size int, q *biz.TimedTask) ([]*biz.TimedTask, int64, error) { + // During first-install the data layer intentionally serves a bootstrap + // database without system tables. The scheduler starts before /init/initdb + // and should remain idle instead of logging a missing-table SQL error. + if !r.data.DatabaseReady() { + return []*biz.TimedTask{}, 0, nil + } db := r.data.DB().WithContext(ctx).Model(&taskPO{}) if q != nil { if q.Name != "" { diff --git a/app/system/internal/data/repository/task_test.go b/app/system/internal/data/repository/task_test.go new file mode 100644 index 0000000..98faf2c --- /dev/null +++ b/app/system/internal/data/repository/task_test.go @@ -0,0 +1,17 @@ +package system + +import ( + "context" + "testing" +) + +func TestTaskRepoListIsEmptyBeforeDatabaseInitialization(t *testing.T) { + repo := NewTaskRepo(&Data{}) + items, total, err := repo.ListTasks(context.Background(), 0, 0, nil) + if err != nil { + t.Fatal(err) + } + if total != 0 || len(items) != 0 { + t.Fatalf("bootstrap tasks = (%d, %d), want empty", total, len(items)) + } +} diff --git a/app/system/internal/server/README.md b/app/system/internal/server/README.md new file mode 100644 index 0000000..51fc608 --- /dev/null +++ b/app/system/internal/server/README.md @@ -0,0 +1,12 @@ +# Server Layer + +The root package owns Gin server lifecycle and Swagger setup. HTTP concerns are +grouped by role: + +- `handler/`: resource handlers and HTTP boundary validation +- `middleware/`: request metadata, auth, access control, audit, recovery, CORS +- `router/`: resource route registration and the system route registrar +- `httpx/`: system adapter for shared response and cookie helpers + +Keep new files in the matching role directory instead of adding transport files +to the root package. diff --git a/app/system/internal/server/router/api.go b/app/system/internal/server/router/api.go index 58c3a86..a306e2a 100644 --- a/app/system/internal/server/router/api.go +++ b/app/system/internal/server/router/api.go @@ -19,7 +19,7 @@ func RegisterAPI(group, public *gin.RouterGroup, engine *gin.Engine, h *API) { api.GET("/syncApi", h.Sync(engine)) api.POST("/ignoreApi", h.Ignore) api.POST("/enterSyncApi", h.ApplySync) - public.Group("/api").GET("/freshCasbin", h.FreshCasbin) + group.Group("/api").GET("/freshCasbin", h.FreshCasbin) casbin := group.Group("/casbin") casbin.POST("/updateCasbin", h.SetPolicyPaths) casbin.POST("/getPolicyPathByAuthorityId", h.PolicyPaths) diff --git a/app/system/internal/service/README.md b/app/system/internal/service/README.md new file mode 100644 index 0000000..18b94b2 --- /dev/null +++ b/app/system/internal/service/README.md @@ -0,0 +1,9 @@ +# Service Layer + +`service` adapts HTTP-facing DTOs to business usecases and owns application +orchestration. Request/response/filter contracts live in `dto/`; the root +package contains resource services, conversions, route metadata, and security +coordination. + +`dto_alias.go` is a compatibility bridge for existing service callers. New +transport code should import `service/dto` directly when it needs a contract.