优化结构

This commit is contained in:
Yvan
2026-08-22 12:13:16 +08:00
parent d987244d0e
commit b27a66e9d3
200 changed files with 1794 additions and 1715 deletions
+21 -10
View File
@@ -4,17 +4,21 @@
package app
import (
systemrouter "kra/internal/server/router"
systemworker "kra/internal/worker"
paymentmodule "kra/internal/modules/payment"
systemmodule "kra/internal/modules/system"
"kra/pkg/module"
platformtask "kra/pkg/task"
)
// Catalog lists the business modules enabled in this binary. Adding an order
// module means adding one Definition here; system initialization and runtime
// code consume the catalog without knowing that module's implementation.
// Catalog lists the business modules enabled in this binary. Static module
// contributions (migrations, admin metadata, default tasks) enter through a
// Definition; runtime routes and dependency-bearing task contributors still
// need explicit wiring below.
func Catalog() module.Catalog {
return module.Catalog{Definitions: []module.Definition{Definition()}}
return module.Catalog{Definitions: []module.Definition{
systemmodule.Definition(),
paymentmodule.Definition(),
}}
}
// TaskRegistry builds the process-wide registry from dependency-free module
@@ -26,8 +30,15 @@ func TaskRegistry(catalog module.Catalog) *platformtask.Registry {
return registry
}
// Runtime composes HTTP route contributors from the enabled modules.
func Runtime(systemRoutes *systemrouter.Routes, systemTasks *systemworker.TaskMethods, registry *platformtask.Registry) *module.Runtime {
platformtask.Apply(registry, systemTasks)
return module.NewRuntime(systemRoutes)
// RuntimeContributions groups runtime objects that need constructed
// dependencies. The binary composition root supplies the concrete modules.
type RuntimeContributions struct {
Routes []module.RouteRegistrar
Tasks []platformtask.Contributor
}
// Runtime activates dependency-bearing tasks and composes module routes.
func Runtime(contributions RuntimeContributions, registry *platformtask.Registry) *module.Runtime {
platformtask.Apply(registry, contributions.Tasks...)
return module.NewRuntime(contributions.Routes...)
}
+74
View File
@@ -0,0 +1,74 @@
package app
import (
"context"
"encoding/json"
"testing"
"github.com/gin-gonic/gin"
"kra/pkg/module"
platformtask "kra/pkg/task"
)
func TestCatalogIncludesSystemDefinition(t *testing.T) {
catalog := Catalog()
if len(catalog.Definitions) != 2 {
t.Fatalf("definitions = %d, want 2", len(catalog.Definitions))
}
if catalog.Definitions[0].Name != "system" || catalog.Definitions[1].Name != "payment" {
t.Fatalf("definition order = [%q, %q], want [system, payment]", catalog.Definitions[0].Name, catalog.Definitions[1].Name)
}
if got := catalog.MigrationSteps(); len(got) != 5 {
t.Fatalf("module migrations = %d, want 5", len(got))
}
if surface := catalog.Surface(); len(surface.Menus) != 3 || len(surface.APIs) != 15 {
t.Fatalf("admin surface = %d menus/%d APIs, want 3/15", len(surface.Menus), len(surface.APIs))
}
if got := catalog.DefaultTimedTasks(); len(got) != 2 {
t.Fatalf("default timed tasks = %d, want 2", len(got))
}
}
func TestTaskRegistryRegistersStaticModuleMethods(t *testing.T) {
method := platformtask.Method{
Name: "test.static",
Run: func(context.Context, json.RawMessage) error { return nil },
}
catalog := module.Catalog{Definitions: []module.Definition{{Tasks: []platformtask.Method{method}}}}
registry := TaskRegistry(catalog)
if _, ok := registry.Lookup(method.Name); !ok {
t.Fatalf("method %q was not registered", method.Name)
}
}
type testRouteRegistrar struct{ called bool }
func (registrar *testRouteRegistrar) RegisterRoutes(*gin.RouterGroup, *gin.RouterGroup, *gin.Engine) {
registrar.called = true
}
type testTaskContributor struct{ name string }
func (contributor testTaskContributor) RegisterTasks(registry *platformtask.Registry) {
registry.Register(platformtask.Method{
Name: contributor.name,
Run: func(context.Context, json.RawMessage) error { return nil },
})
}
func TestRuntimeAppliesAllContributions(t *testing.T) {
registry := platformtask.NewRegistry()
route := &testRouteRegistrar{}
runtime := Runtime(RuntimeContributions{
Routes: []module.RouteRegistrar{route},
Tasks: []platformtask.Contributor{testTaskContributor{name: "test.runtime"}},
}, registry)
if _, ok := registry.Lookup("test.runtime"); !ok {
t.Fatal("runtime task contributor was not applied")
}
runtime.RegisterRoutes(nil, nil, nil)
if !route.called {
t.Fatal("runtime route contributor was not called")
}
}
-34
View File
@@ -1,34 +0,0 @@
package app
import (
datapayment "kra/internal/data/payment"
datasystem "kra/internal/data/repository"
"kra/pkg/module"
)
// Definition describes the built-in system contribution to the application
// catalog. Other business modules can expose the same shape independently.
func Definition() module.Definition {
surface := datapayment.AdminSurface()
communication := module.Surface{
Menus: []module.Menu{{Name: "integrationConfig", Path: "integrationConfig", ParentName: "extensions", Component: "view/systemTools/integration/config.vue", Title: "通信集成", Icon: "connection", Sort: 8}},
APIs: []module.API{
{Path: "/integration/configs/:kind", Method: "GET", Group: "集成配置", Description: "按类型获取集成配置"},
{Path: "/integration/configs/:kind/:provider", Method: "GET", Group: "集成配置", Description: "获取指定集成配置"},
{Path: "/integration/configs/:kind/:provider", Method: "PUT", Group: "集成配置", Description: "保存集成配置"},
{Path: "/integration/configs/:kind/:provider/test", Method: "POST", Group: "集成配置", Description: "测试通信集成连接"},
{Path: "/integration/configs/:kind/:provider", Method: "DELETE", Group: "集成配置", Description: "删除集成配置"},
},
}
surface.Menus = append(surface.Menus, communication.Menus...)
surface.APIs = append(surface.APIs, communication.APIs...)
return module.Definition{
Name: "system",
Migrations: append(datasystem.Migrations(), datapayment.Migrations()...),
Surface: surface,
TimedTasks: []module.TimedTask{
{Name: "ClearDB", Description: "定时清理数据库过期日志(操作记录/JWT黑名单/定时任务执行日志)", Spec: "@daily", MethodName: "ClearDB", Enabled: true},
{Name: "CleanStaleUploads", Description: "定时清理过期大文件上传会话", Spec: "@hourly", MethodName: "CleanStaleUploads", Enabled: true},
},
}
}
-27
View File
@@ -1,27 +0,0 @@
package app
import "testing"
func TestDefinitionIncludesCommunicationIntegrationSurface(t *testing.T) {
surface := Definition().Surface
menuFound := false
for _, menu := range surface.Menus {
if menu.Name == "integrationConfig" {
menuFound = menu.ParentName == "extensions" && menu.Component == "view/systemTools/integration/config.vue"
break
}
}
if !menuFound {
t.Fatal("communication integration menu is missing")
}
apiFound := false
for _, api := range surface.APIs {
if api.Method == "PUT" && api.Path == "/integration/configs/:kind/:provider" {
apiFound = true
break
}
}
if !apiFound {
t.Fatal("communication integration save API is missing")
}
}