优化结构

This commit is contained in:
Yvan 2026-08-22 21:39:39 +08:00
parent 699bcceeea
commit 0126995090
17 changed files with 61 additions and 52 deletions

View File

@ -19,14 +19,16 @@
## system 内部保留边界 ## system 内部保留边界
- `app`:组合根,汇总各模块的迁移、菜单、路由和任务贡献。 - `app`:组合根,汇总各模块的迁移、菜单、路由和任务贡献。
- `modules/system`system 模块的 Definition声明迁移、管理面和默认任务。 - `modules/system`system 模块的 Definition声明系统表迁移。
- `modules/integration`integration 配置迁移和管理面贡献。
- `modules/task`:定时任务迁移和默认任务贡献。
- `modules/payment`payment 模块的 Definition声明支付迁移和支付管理面。 - `modules/payment`payment 模块的 Definition声明支付迁移和支付管理面。
- `biz/system`:用户、权限、菜单、审计、媒体和系统配置等系统领域模型与用例。 - `biz/system`:用户、权限、菜单、审计、媒体和系统配置等系统领域模型与用例。
- `biz/payment`:支付订单、支付流程、支付接口和支付日志。 - `biz/payment`:支付订单、支付流程、支付接口和支付日志。
- `biz/integration`:支付/消息队列/WebSocket 集成配置定义与校验。 - `biz/integration`:支付/消息队列/WebSocket 集成配置定义与校验。
- `biz/task`:定时任务模型、任务用例和任务注册协议。 - `biz/task`:定时任务模型、任务用例和任务注册协议。
- `conf`system 配置 proto、运行时快照和生成代码。 - `conf`system 配置 proto、运行时快照和生成代码。
- `data`数据库连接、PO、仓储、system 表、支付持久化和配置 watcher - `data`共享数据库生命周期与配置 watcherPO/仓储按 `system`、`integration`、`task`、`payment` 子包隔离
- `initialize`:首次安装、配置迁移、种子编排和运行时重载。 - `initialize`:首次安装、配置迁移、种子编排和运行时重载。
- `integration`Redis、邮件、存储、支付、WebSocket、EMQX 和 RabbitMQ 的 provider 生命周期。 - `integration`Redis、邮件、存储、支付、WebSocket、EMQX 和 RabbitMQ 的 provider 生命周期。
- `security`JWT claims、签发/解析和后台安全实现。 - `security`JWT claims、签发/解析和后台安全实现。
@ -50,7 +52,11 @@ internal/
integration/# 集成配置领域 integration/# 集成配置领域
task/ # 定时任务领域 task/ # 定时任务领域
conf/ # 配置 proto/runtime conf/ # 配置 proto/runtime
data/ # PO、repo、数据库和迁移 data/
system/ # 系统表与系统仓储
integration/# 集成配置表与仓储
task/ # 定时任务表与仓储
payment/ # 支付表与仓储
initialize/ # 首次安装和配置编排 initialize/ # 首次安装和配置编排
integration/ # 外部 I/O provider integration/ # 外部 I/O provider
security/ # JWT 和安全实现 security/ # JWT 和安全实现

View File

@ -4,13 +4,13 @@
生命周期或分层职责的包: 生命周期或分层职责的包:
- `app`:组合根、模块 catalog 和任务/路由运行时组合 - `app`:组合根、模块 catalog 和任务/路由运行时组合
- `modules`:按业务模块维护 Definition 等模块贡献 - `modules`:按 system/integration/task/payment 维护 Definition 等模块贡献
- `biz/system`:用户、权限、菜单、审计、媒体和系统配置领域 - `biz/system`:用户、权限、菜单、审计、媒体和系统配置领域
- `biz/payment`:支付订单、支付流程、支付接口和支付日志 - `biz/payment`:支付订单、支付流程、支付接口和支付日志
- `biz/integration`:集成配置定义、校验和连接测试边界 - `biz/integration`:集成配置定义、校验和连接测试边界
- `biz/task`:定时任务模型、用例和任务注册协议 - `biz/task`:定时任务模型、用例和任务注册协议
- `conf`:基础配置 proto 与运行时配置解析 - `conf`:基础配置 proto 与运行时配置解析
- `data`数据库生命周期、系统仓储、系统表和支付持久化 - `data`共享数据库生命周期;仓储按 `data/system`、`data/integration`、`data/task`、`data/payment` 隔离
- `initialize`:数据库首次初始化和系统种子数据编排 - `initialize`:数据库首次初始化和系统种子数据编排
- `integration`Redis、邮件、对象存储、支付、WebSocket、EMQX 和 RabbitMQ 适配器 - `integration`Redis、邮件、对象存储、支付、WebSocket、EMQX 和 RabbitMQ 适配器
- `routecatalog`:统一声明 HTTP 路由的公开性、操作审计、请求体策略和 API 元数据 - `routecatalog`:统一声明 HTTP 路由的公开性、操作审计、请求体策略和 API 元数据

View File

@ -58,7 +58,7 @@ type TaskRepo interface {
ToggleTask(context.Context, uint, bool) error ToggleTask(context.Context, uint, bool) error
RecordTaskLog(context.Context, *TimedTaskLog) error RecordTaskLog(context.Context, *TimedTaskLog) error
ListTaskLogs(context.Context, int, int, uint, string) ([]*TimedTaskLog, int64, error) ListTaskLogs(context.Context, int, int, uint, string) ([]*TimedTaskLog, int64, error)
CleanupLogs(context.Context) error CleanupTaskLogs(context.Context) error
TaskNameExists(context.Context, string, uint) (bool, error) TaskNameExists(context.Context, string, uint) (bool, error)
} }

View File

@ -65,7 +65,7 @@ func (r *applicationTaskRepo) RecordTaskLog(context.Context, *TimedTaskLog) erro
func (r *applicationTaskRepo) ListTaskLogs(context.Context, int, int, uint, string) ([]*TimedTaskLog, int64, error) { func (r *applicationTaskRepo) ListTaskLogs(context.Context, int, int, uint, string) ([]*TimedTaskLog, int64, error) {
return nil, 0, nil return nil, 0, nil
} }
func (r *applicationTaskRepo) CleanupLogs(context.Context) error { return nil } func (r *applicationTaskRepo) CleanupTaskLogs(context.Context) error { return nil }
func (r *applicationTaskRepo) TaskNameExists(context.Context, string, uint) (bool, error) { func (r *applicationTaskRepo) TaskNameExists(context.Context, string, uint) (bool, error) {
return r.nameExists, r.nameExistsErr return r.nameExists, r.nameExistsErr
} }

View File

@ -12,7 +12,7 @@ watching, and repository implementations.
- root files: shared database lifecycle, runtime clients, configuration - root files: shared database lifecycle, runtime clients, configuration
orchestration, data-scope auditing, and migration orchestration orchestration, data-scope auditing, and migration orchestration
新增数据模块时创建 `internal/data/<module>`,提供 `ProviderSet()` 和 新增数据模块时创建 `internal/data/<module>`,提供 `ProviderSet` 和
`Migrations()`,再在 `internal/modules/<module>/definition.go` 注册迁移与 `Migrations()`,再在 `internal/modules/<module>/definition.go` 注册迁移与
管理面,最后在根 `data.ProviderSet` 中注册该模块。根 `data` 不应直接 管理面,最后在根 `data.ProviderSet` 中注册该模块。根 `data` 不应直接
拥有业务表 PO也不应让一个模块引用另一个模块的私有 PO。 拥有业务表 PO也不应让一个模块引用另一个模块的私有 PO。

View File

@ -18,7 +18,6 @@ import (
const ( const (
integrationKindStorage = "storage" integrationKindStorage = "storage"
integrationKindEmail = "email" integrationKindEmail = "email"
integrationKindPayment = "payment"
) )
var storageProviderNames = []string{ var storageProviderNames = []string{

View File

@ -7,7 +7,9 @@ import (
"strings" "strings"
"testing" "testing"
integrationbiz "kra/internal/biz/integration"
"kra/internal/conf" "kra/internal/conf"
dataintegration "kra/internal/data/integration"
"kra/internal/integration/storage" "kra/internal/integration/storage"
"google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/encoding/protojson"
@ -73,7 +75,7 @@ func TestStorageIntegrationConfigRoundTrip(t *testing.T) {
if err := saveEmailIntegrationConfig(db, email); err != nil { if err := saveEmailIntegrationConfig(db, email); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := db.Create(&dataintegration.ConfigPO{Kind: integrationKindPayment, Provider: "wechat-pay", Config: `{"merchant_id":"123"}`}).Error; err != nil { if err := db.Create(&dataintegration.ConfigPO{Kind: integrationbiz.IntegrationKindPayment, Provider: "wechat-pay", Config: `{"merchant_id":"123"}`}).Error; err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -106,7 +108,7 @@ func TestStorageIntegrationConfigRoundTrip(t *testing.T) {
if err = db.Model(&dataintegration.ConfigPO{}).Where("kind = ?", integrationKindStorage).Count(&storageCount).Error; err != nil { if err = db.Model(&dataintegration.ConfigPO{}).Where("kind = ?", integrationKindStorage).Count(&storageCount).Error; err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err = db.Model(&dataintegration.ConfigPO{}).Where("kind = ?", integrationKindPayment).Count(&paymentCount).Error; err != nil { if err = db.Model(&dataintegration.ConfigPO{}).Where("kind = ?", integrationbiz.IntegrationKindPayment).Count(&paymentCount).Error; err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err = db.Model(&dataintegration.ConfigPO{}).Where("kind = ?", integrationKindEmail).Count(&emailCount).Error; err != nil { if err = db.Model(&dataintegration.ConfigPO{}).Where("kind = ?", integrationKindEmail).Count(&emailCount).Error; err != nil {

View File

@ -7,15 +7,9 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
func InfrastructureMigrations() []migration.Step {
return nil
}
// migrateAll is the single data-layer migration entry point. Every module // migrateAll is the single data-layer migration entry point. Every module
// must register its migrations through the application catalog; there is no // must register its migrations through the application catalog; there is no
// hidden system/payment fallback that could silently omit a new module. // hidden system/payment fallback that could silently omit a new module.
func migrateAll(db *gorm.DB, catalog module.Catalog) error { func migrateAll(db *gorm.DB, catalog module.Catalog) error {
steps := append([]migration.Step{}, InfrastructureMigrations()...) return migration.Run(db, catalog.MigrationSteps())
steps = append(steps, catalog.MigrationSteps()...)
return migration.Run(db, steps)
} }

View File

@ -3,6 +3,7 @@ package data
import ( import (
"testing" "testing"
dataintegration "kra/internal/data/integration"
integrationmodule "kra/internal/modules/integration" integrationmodule "kra/internal/modules/integration"
paymentmodule "kra/internal/modules/payment" paymentmodule "kra/internal/modules/payment"
systemmodule "kra/internal/modules/system" systemmodule "kra/internal/modules/system"

View File

@ -1,5 +0,0 @@
package payment
import dataintegration "kra/internal/data/integration"
const integrationKindPayment = "payment"

View File

@ -9,6 +9,7 @@ import (
"fmt" "fmt"
integrationbiz "kra/internal/biz/integration" integrationbiz "kra/internal/biz/integration"
bizpayment "kra/internal/biz/payment" bizpayment "kra/internal/biz/payment"
dataintegration "kra/internal/data/integration"
"net/url" "net/url"
"strconv" "strconv"
"strings" "strings"
@ -27,11 +28,11 @@ func NewPaymentRepo(data Provider) bizpayment.PaymentRepo { return &paymentRepo{
func ensurePaymentIntegrationConfigs(db *gorm.DB) error { func ensurePaymentIntegrationConfigs(db *gorm.DB) error {
for _, provider := range bizpayment.SupportedPaymentProviders { for _, provider := range bizpayment.SupportedPaymentProviders {
var row dataintegration.ConfigPO var row dataintegration.ConfigPO
err := db.Where("kind = ? AND provider = ?", integrationKindPayment, provider).First(&row).Error err := db.Where("kind = ? AND provider = ?", integrationbiz.IntegrationKindPayment, provider).First(&row).Error
defaults := integrationbiz.DefaultIntegrationConfig(integrationKindPayment, provider) defaults := integrationbiz.DefaultIntegrationConfig(integrationbiz.IntegrationKindPayment, provider)
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
encoded, _ := json.Marshal(defaults) encoded, _ := json.Marshal(defaults)
if err := db.Create(&dataintegration.ConfigPO{Kind: integrationKindPayment, Provider: provider, Enabled: false, Config: string(encoded)}).Error; err != nil { if err := db.Create(&dataintegration.ConfigPO{Kind: integrationbiz.IntegrationKindPayment, Provider: provider, Enabled: false, Config: string(encoded)}).Error; err != nil {
return err return err
} }
continue continue
@ -60,7 +61,7 @@ func ensurePaymentIntegrationConfigs(db *gorm.DB) error {
func (r *paymentRepo) row(ctx context.Context, provider string) (*dataintegration.ConfigPO, map[string]any, error) { func (r *paymentRepo) row(ctx context.Context, provider string) (*dataintegration.ConfigPO, map[string]any, error) {
var row dataintegration.ConfigPO var row dataintegration.ConfigPO
if err := r.data.DB().WithContext(ctx).Where("kind = ? AND provider = ?", integrationKindPayment, provider).First(&row).Error; err != nil { if err := r.data.DB().WithContext(ctx).Where("kind = ? AND provider = ?", integrationbiz.IntegrationKindPayment, provider).First(&row).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil, bizpayment.ErrPaymentProviderNotFound return nil, nil, bizpayment.ErrPaymentProviderNotFound
} }
@ -299,7 +300,7 @@ func recordPaymentTestError(ctx context.Context, data Provider, provider, tradeN
func (r *paymentRepo) testRow(ctx context.Context, provider string) (map[string]any, error) { func (r *paymentRepo) testRow(ctx context.Context, provider string) (map[string]any, error) {
var row dataintegration.ConfigPO var row dataintegration.ConfigPO
if err := r.data.DB().WithContext(ctx).Where("kind = ? AND provider = ?", integrationKindPayment, provider).First(&row).Error; err != nil { if err := r.data.DB().WithContext(ctx).Where("kind = ? AND provider = ?", integrationbiz.IntegrationKindPayment, provider).First(&row).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, bizpayment.ErrPaymentProviderNotFound return nil, bizpayment.ErrPaymentProviderNotFound
} }

View File

@ -6,6 +6,7 @@ import (
"github.com/glebarez/sqlite" "github.com/glebarez/sqlite"
"gorm.io/gorm" "gorm.io/gorm"
dataintegration "kra/internal/data/integration"
) )
type Data struct{ gormDB *reloadableDB } type Data struct{ gormDB *reloadableDB }

View File

@ -14,7 +14,7 @@ func Migrations() []migration.Step {
return []migration.Step{{ return []migration.Step{{
ID: "202608220001_task_schema", ID: "202608220001_task_schema",
Migrate: func(db *gorm.DB) error { Migrate: func(db *gorm.DB) error {
return migration.CreateMissingTables(db, &TaskPO{}, &TaskLogPO{}) return migration.CreateMissingTables(db, &taskPO{}, &taskLogPO{})
}, },
}} }}
} }
@ -36,7 +36,7 @@ func SeedDefaults(ctx context.Context, db *gorm.DB, defaults []platformmodule.Ti
if strings.TrimSpace(item.Name) == "" { if strings.TrimSpace(item.Name) == "" {
continue continue
} }
row := TaskPO{Name: item.Name, Description: item.Description, Spec: item.Spec, WithSeconds: item.WithSeconds, ExecutorType: "method", MethodName: item.MethodName, Enabled: item.Enabled} row := taskPO{Name: item.Name, Description: item.Description, Spec: item.Spec, WithSeconds: item.WithSeconds, ExecutorType: "method", MethodName: item.MethodName, Enabled: item.Enabled}
if err := tx.Where("name = ?", row.Name).FirstOrCreate(&row).Error; err != nil { if err := tx.Where("name = ?", row.Name).FirstOrCreate(&row).Error; err != nil {
return err return err
} }

View File

@ -10,7 +10,7 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
type TaskPO struct { type taskPO struct {
ID uint `gorm:"primaryKey"` ID uint `gorm:"primaryKey"`
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
@ -27,9 +27,9 @@ type TaskPO struct {
HTTPAllowPrivate, Enabled bool HTTPAllowPrivate, Enabled bool
} }
func (TaskPO) TableName() string { return "sys_timed_tasks" } func (taskPO) TableName() string { return "sys_timed_tasks" }
type TaskLogPO struct { type taskLogPO struct {
ID uint `gorm:"primaryKey"` ID uint `gorm:"primaryKey"`
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
@ -42,15 +42,15 @@ type TaskLogPO struct {
ErrorMsg, Output string `gorm:"type:text"` ErrorMsg, Output string `gorm:"type:text"`
} }
func (TaskLogPO) TableName() string { return "sys_timed_task_logs" } func (taskLogPO) TableName() string { return "sys_timed_task_logs" }
type taskRepo struct{ data Provider } type taskRepo struct{ data Provider }
func NewTaskRepo(data Provider) taskbiz.TaskRepo { return &taskRepo{data: data} } func NewTaskRepo(data Provider) taskbiz.TaskRepo { return &taskRepo{data: data} }
func taskToPO(v *taskbiz.TimedTask) TaskPO { func taskToPO(v *taskbiz.TimedTask) taskPO {
return TaskPO{ID: v.ID, Name: v.Name, Description: v.Description, Spec: v.Spec, WithSeconds: v.WithSeconds, ExecutorType: v.ExecutorType, MethodName: v.MethodName, Params: gormkit.JSON(v.Params), HTTPURL: v.HTTPURL, HTTPMethod: v.HTTPMethod, HTTPHeader: gormkit.JSON(v.HTTPHeader), HTTPBody: v.HTTPBody, HTTPAllowPrivate: v.HTTPAllowPrivate, Enabled: v.Enabled} return taskPO{ID: v.ID, Name: v.Name, Description: v.Description, Spec: v.Spec, WithSeconds: v.WithSeconds, ExecutorType: v.ExecutorType, MethodName: v.MethodName, Params: gormkit.JSON(v.Params), HTTPURL: v.HTTPURL, HTTPMethod: v.HTTPMethod, HTTPHeader: gormkit.JSON(v.HTTPHeader), HTTPBody: v.HTTPBody, HTTPAllowPrivate: v.HTTPAllowPrivate, Enabled: v.Enabled}
} }
func taskFromPO(v TaskPO) *taskbiz.TimedTask { func taskFromPO(v taskPO) *taskbiz.TimedTask {
return &taskbiz.TimedTask{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, Name: v.Name, Description: v.Description, Spec: v.Spec, WithSeconds: v.WithSeconds, ExecutorType: v.ExecutorType, MethodName: v.MethodName, Params: []byte(v.Params), HTTPURL: v.HTTPURL, HTTPMethod: v.HTTPMethod, HTTPHeader: []byte(v.HTTPHeader), HTTPBody: v.HTTPBody, HTTPAllowPrivate: v.HTTPAllowPrivate, Enabled: v.Enabled} return &taskbiz.TimedTask{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, Name: v.Name, Description: v.Description, Spec: v.Spec, WithSeconds: v.WithSeconds, ExecutorType: v.ExecutorType, MethodName: v.MethodName, Params: []byte(v.Params), HTTPURL: v.HTTPURL, HTTPMethod: v.HTTPMethod, HTTPHeader: []byte(v.HTTPHeader), HTTPBody: v.HTTPBody, HTTPAllowPrivate: v.HTTPAllowPrivate, Enabled: v.Enabled}
} }
func (r *taskRepo) CreateTask(ctx context.Context, v *taskbiz.TimedTask) error { func (r *taskRepo) CreateTask(ctx context.Context, v *taskbiz.TimedTask) error {
@ -63,7 +63,7 @@ func (r *taskRepo) CreateTask(ctx context.Context, v *taskbiz.TimedTask) error {
} }
func (r *taskRepo) TaskNameExists(ctx context.Context, name string, excludeID uint) (bool, error) { func (r *taskRepo) TaskNameExists(ctx context.Context, name string, excludeID uint) (bool, error) {
var count int64 var count int64
db := r.data.DB().WithContext(ctx).Model(&TaskPO{}).Where("name = ?", name) db := r.data.DB().WithContext(ctx).Model(&taskPO{}).Where("name = ?", name)
if excludeID > 0 { if excludeID > 0 {
db = db.Where("id <> ?", excludeID) db = db.Where("id <> ?", excludeID)
} }
@ -72,13 +72,13 @@ func (r *taskRepo) TaskNameExists(ctx context.Context, name string, excludeID ui
} }
func (r *taskRepo) UpdateTask(ctx context.Context, v *taskbiz.TimedTask) error { func (r *taskRepo) UpdateTask(ctx context.Context, v *taskbiz.TimedTask) error {
po := taskToPO(v) po := taskToPO(v)
return r.data.DB().WithContext(ctx).Model(&TaskPO{}).Where("id = ?", v.ID).Select("name", "description", "spec", "with_seconds", "executor_type", "method_name", "params", "http_url", "http_method", "http_header", "http_body", "http_allow_private", "enabled").Updates(&po).Error return r.data.DB().WithContext(ctx).Model(&taskPO{}).Where("id = ?", v.ID).Select("name", "description", "spec", "with_seconds", "executor_type", "method_name", "params", "http_url", "http_method", "http_header", "http_body", "http_allow_private", "enabled").Updates(&po).Error
} }
func (r *taskRepo) DeleteTask(ctx context.Context, id uint) error { func (r *taskRepo) DeleteTask(ctx context.Context, id uint) error {
return r.data.DB().WithContext(ctx).Delete(&TaskPO{}, id).Error return r.data.DB().WithContext(ctx).Delete(&taskPO{}, id).Error
} }
func (r *taskRepo) FindTask(ctx context.Context, id uint) (*taskbiz.TimedTask, error) { func (r *taskRepo) FindTask(ctx context.Context, id uint) (*taskbiz.TimedTask, error) {
var po TaskPO var po taskPO
if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil { if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil {
return nil, err return nil, err
} }
@ -91,7 +91,7 @@ func (r *taskRepo) ListTasks(ctx context.Context, page, size int, q *taskbiz.Tim
if !r.data.DatabaseReady() { if !r.data.DatabaseReady() {
return []*taskbiz.TimedTask{}, 0, nil return []*taskbiz.TimedTask{}, 0, nil
} }
db := r.data.DB().WithContext(ctx).Model(&TaskPO{}) db := r.data.DB().WithContext(ctx).Model(&taskPO{})
if q != nil { if q != nil {
if q.Name != "" { if q.Name != "" {
db = db.Where("name LIKE ?", "%"+q.Name+"%") db = db.Where("name LIKE ?", "%"+q.Name+"%")
@ -107,7 +107,7 @@ func (r *taskRepo) ListTasks(ctx context.Context, page, size int, q *taskbiz.Tim
if err := db.Count(&total).Error; err != nil { if err := db.Count(&total).Error; err != nil {
return nil, 0, err return nil, 0, err
} }
var pos []TaskPO var pos []taskPO
if err := pagination.Apply(db.Order("id desc"), page, size, 100).Find(&pos).Error; err != nil { if err := pagination.Apply(db.Order("id desc"), page, size, 100).Find(&pos).Error; err != nil {
return nil, 0, err return nil, 0, err
} }
@ -118,16 +118,16 @@ func (r *taskRepo) ListTasks(ctx context.Context, page, size int, q *taskbiz.Tim
return out, total, nil return out, total, nil
} }
func (r *taskRepo) ToggleTask(ctx context.Context, id uint, enabled bool) error { func (r *taskRepo) ToggleTask(ctx context.Context, id uint, enabled bool) error {
return r.data.DB().WithContext(ctx).Model(&TaskPO{}).Where("id = ?", id).Update("enabled", enabled).Error return r.data.DB().WithContext(ctx).Model(&taskPO{}).Where("id = ?", id).Update("enabled", enabled).Error
} }
func (r *taskRepo) RecordTaskLog(ctx context.Context, v *taskbiz.TimedTaskLog) error { func (r *taskRepo) RecordTaskLog(ctx context.Context, v *taskbiz.TimedTaskLog) error {
return r.data.DB().WithContext(ctx).Create(&TaskLogPO{TaskID: v.TaskID, TaskName: v.TaskName, TriggerType: v.TriggerType, StartedAt: v.StartedAt, FinishedAt: v.FinishedAt, DurationMS: v.DurationMS, Status: v.Status, ErrorMsg: v.ErrorMsg, Output: v.Output}).Error return r.data.DB().WithContext(ctx).Create(&taskLogPO{TaskID: v.TaskID, TaskName: v.TaskName, TriggerType: v.TriggerType, StartedAt: v.StartedAt, FinishedAt: v.FinishedAt, DurationMS: v.DurationMS, Status: v.Status, ErrorMsg: v.ErrorMsg, Output: v.Output}).Error
} }
func taskLogFromPO(v TaskLogPO) *taskbiz.TimedTaskLog { func taskLogFromPO(v taskLogPO) *taskbiz.TimedTaskLog {
return &taskbiz.TimedTaskLog{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, TaskID: v.TaskID, TaskName: v.TaskName, TriggerType: v.TriggerType, StartedAt: v.StartedAt, FinishedAt: v.FinishedAt, DurationMS: v.DurationMS, Status: v.Status, ErrorMsg: v.ErrorMsg, Output: v.Output} return &taskbiz.TimedTaskLog{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, TaskID: v.TaskID, TaskName: v.TaskName, TriggerType: v.TriggerType, StartedAt: v.StartedAt, FinishedAt: v.FinishedAt, DurationMS: v.DurationMS, Status: v.Status, ErrorMsg: v.ErrorMsg, Output: v.Output}
} }
func (r *taskRepo) ListTaskLogs(ctx context.Context, page, size int, taskID uint, status string) ([]*taskbiz.TimedTaskLog, int64, error) { func (r *taskRepo) ListTaskLogs(ctx context.Context, page, size int, taskID uint, status string) ([]*taskbiz.TimedTaskLog, int64, error) {
db := r.data.DB().WithContext(ctx).Model(&TaskLogPO{}) db := r.data.DB().WithContext(ctx).Model(&taskLogPO{})
if taskID != 0 { if taskID != 0 {
db = db.Where("task_id = ?", taskID) db = db.Where("task_id = ?", taskID)
} }
@ -138,7 +138,7 @@ func (r *taskRepo) ListTaskLogs(ctx context.Context, page, size int, taskID uint
if err := db.Count(&total).Error; err != nil { if err := db.Count(&total).Error; err != nil {
return nil, 0, err return nil, 0, err
} }
var pos []TaskLogPO var pos []taskLogPO
if err := pagination.Apply(db.Order("id desc"), page, size, 100).Find(&pos).Error; err != nil { if err := pagination.Apply(db.Order("id desc"), page, size, 100).Find(&pos).Error; err != nil {
return nil, 0, err return nil, 0, err
} }
@ -148,8 +148,8 @@ func (r *taskRepo) ListTaskLogs(ctx context.Context, page, size int, taskID uint
} }
return out, total, nil return out, total, nil
} }
func (r *taskRepo) CleanupLogs(ctx context.Context) error { func (r *taskRepo) CleanupTaskLogs(ctx context.Context) error {
return r.data.DB().WithContext(ctx).Unscoped(). return r.data.DB().WithContext(ctx).Unscoped().
Where("created_at < ?", time.Now().Add(-720*time.Hour)). Where("created_at < ?", time.Now().Add(-720*time.Hour)).
Delete(&TaskLogPO{}).Error Delete(&taskLogPO{}).Error
} }

View File

@ -0,0 +1,10 @@
package task
import "testing"
func TestDefinitionOwnsTaskPersistenceAndDefaults(t *testing.T) {
definition := Definition()
if definition.Name != "task" || len(definition.Migrations) != 1 || len(definition.TimedTasks) != 2 {
t.Fatalf("task definition = %#v", definition)
}
}

View File

@ -34,7 +34,7 @@ func (methods *TaskMethods) RegisterTasks(registry *platformtask.Registry) {
if err := methods.maintenance.CleanupExpired(ctx); err != nil { if err := methods.maintenance.CleanupExpired(ctx); err != nil {
return err return err
} }
return methods.tasks.CleanupLogs(ctx) return methods.tasks.CleanupTaskLogs(ctx)
}, },
}) })
registry.Register(platformtask.Method{ registry.Register(platformtask.Method{

View File

@ -25,7 +25,7 @@ func (r *workerTaskRepo) CreateTask(context.Context, *taskbiz.TimedTask) error {
func (r *workerTaskRepo) UpdateTask(context.Context, *taskbiz.TimedTask) error { return nil } func (r *workerTaskRepo) UpdateTask(context.Context, *taskbiz.TimedTask) error { return nil }
func (r *workerTaskRepo) DeleteTask(context.Context, uint) error { return nil } func (r *workerTaskRepo) DeleteTask(context.Context, uint) error { return nil }
func (r *workerTaskRepo) ToggleTask(context.Context, uint, bool) error { return nil } func (r *workerTaskRepo) ToggleTask(context.Context, uint, bool) error { return nil }
func (r *workerTaskRepo) CleanupLogs(context.Context) error { return nil } func (r *workerTaskRepo) CleanupTaskLogs(context.Context) error { return nil }
func (r *workerTaskRepo) TaskNameExists(context.Context, string, uint) (bool, error) { func (r *workerTaskRepo) TaskNameExists(context.Context, string, uint) (bool, error) {
return false, nil return false, nil
} }