优化结构

This commit is contained in:
Yvan 2026-08-28 16:37:53 +08:00
parent 295ee5c49e
commit 6f9908d926
14 changed files with 56 additions and 58 deletions

View File

@ -73,33 +73,33 @@
| 文件 | 行数 | 问题 | 合并目标 | | 文件 | 行数 | 问题 | 合并目标 |
|---|---|---|---| |---|---|---|---|
| internal/data/data_scope_record.go | 9 | 单行类型别名,包内 13 处使用 | 并入 data/data_scope.go | | ~~internal/data/data_scope_record.go~~ | ~~9~~ | ~~单行类型别名,包内 13 处使用~~ | ~~已并入 `internal/data/data_scope.go`,行为不变~~ |
| internal/service/dto/authentication.go | 8 | 仅 LoginResponse 1 个类型LoginRequest 在 system.go | 并入 dto/system.go | | ~~internal/service/dto/authentication.go~~ | ~~8~~ | ~~仅 LoginResponse 1 个类型LoginRequest 在 system.go~~ | ~~已并入 `internal/service/dto/system.go`,避免单类型文件~~ |
| internal/data/task/provider.go 别名层 | 11 | `type Provider = dataprovider.Database` 纯转手别名 | 删别名直接用 dataprovider.Database | | ~~internal/data/task/provider.go 别名层~~ | ~~11~~ | ~~`type Provider = dataprovider.Database` 纯转手别名~~ | ~~已删除别名,任务仓储直接依赖 `dataprovider.Database`ProviderSet 保留为 Wire 注入边界~~ |
| internal/biz/task/task_registry.go 别名行 | 11 | TaskMethodFunc/TaskMethod 纯别名零增值(窄接口保留) | 删 :5-6 别名行 | | ~~internal/biz/task/task_registry.go 别名行~~ | ~~11~~ | ~~TaskMethodFunc/TaskMethod 纯别名零增值(窄接口保留)~~ | ~~已删除别名行,接口直接使用 `pkg/task` 类型;窄接口本身保留~~ |
### 5.2 轻度过拆建议合并5 项) ### 5.2 轻度过拆建议合并5 项)
| 文件 | 行数 | 问题 | 建议 | | 文件 | 行数 | 问题 | 建议 |
|---|---|---|---| |---|---|---|---|
| handler/navigation.go | 26 | 仅 Menu 一个端点,依赖 UserService 与 user.go 相同 | 并入 user.go | | ~~handler/navigation.go~~ | ~~26~~ | ~~仅 Menu 一个端点,依赖 UserService 与 user.go 相同~~ | ~~复核后保留:导航是独立路由资源,单独构造器使路由注册和权限边界清晰;合并只减少文件数,不减少复杂度~~ |
| handler/session.go | 25 | 仅 Logout 一个端点 | 并入 user.go 或 public.go | | ~~handler/session.go~~ | ~~25~~ | ~~仅 Logout 一个端点~~ | ~~复核后保留:会话令牌生命周期与用户 CRUD/菜单职责不同,独立 handler 避免 User 结构继续膨胀~~ |
| handler/http.go | 31 | 转发不一致(双风格) | 删转发或统一 | | ~~handler/http.go~~ | ~~31~~ | ~~转发不一致(双风格)~~ | ~~复核后保留并统一为 handler 包唯一响应词汇;这些别名是 transport 边界适配,不是重复业务实现~~ |
| data/system/time.go | 15 | deletedAtPointer 单函数 | 并入 models.go | | ~~data/system/time.go~~ | ~~15~~ | ~~deletedAtPointer 单函数~~ | ~~复核后保留:转换辅助函数与 PO 模型分离,便于软删除映射测试,合并收益仅为文件级减法~~ |
| data/system/audit.go | 17 | 3 个 repo 构造器与实现跨文件分离 | 构造器归位首个实现文件 | | ~~data/system/audit.go~~ | ~~17~~ | ~~3 个 repo 构造器与实现跨文件分离~~ | ~~复核后保留:三个仓储分别对应三个 biz 接口,集中构造器符合 Wire ProviderSet未形成额外抽象~~ |
### 5.3 可选合并2 组) ### 5.3 可选合并2 组)
- router 21 个微文件 → 并入 routes.go 约 420 行 - ~~router 21 个微文件 → 并入 routes.go 约 420 行~~:复核后保留,按资源拆分与 routecatalog 元数据一一对应,便于增删路由和审查权限。
- modules 4 个 definition 包 → 可合并单文件 ~110 行 - ~~modules 4 个 definition 包 → 可合并单文件 ~110 行~~:复核后保留,模块定义是独立注册单元,合并会扩大依赖面且削弱模块边界。
### 5.4 接口碎片化 ### 5.4 接口碎片化
- 同一 DB seam 三套名字、同包双 seam - ~~同一 DB seam 三套名字、同包双 seam~~:复核后保留。`data.Provider`、`system.Provider`、`DatabaseProvider` 分别承载父容器、系统仓储和 Wire 绑定边界;强行统一会让 biz/data 依赖反向耦合,属于接口职责差异而非无效重复。
### 5.5 判定为合理的(防误报) ### 5.5 判定为合理的(防误报)
- modules/surface编译级硬约束循环导入单文件职责完整包 24 个biz 域微文件/dto 微文件/service 对称微文件 - ~~modules/surface编译级硬约束循环导入单文件职责完整包 24 个biz 域微文件/dto 微文件/service 对称微文件~~:确认属于合理拆分,继续保留。
## 审查后认为合理、不建议改动的部分(历轮评估保留决策汇总) ## 审查后认为合理、不建议改动的部分(历轮评估保留决策汇总)

View File

@ -97,7 +97,7 @@ func (uc *MediaUsecase) InitUpload(ctx context.Context, userID uint, name, hash
completed, err := uc.FindCompletedSession(ctx, userID, strings.ToLower(hash)) completed, err := uc.FindCompletedSession(ctx, userID, strings.ToLower(hash))
if err == nil && completed.MediaID != 0 { if err == nil && completed.MediaID != 0 {
if media, findErr := uc.FindMedia(ctx, completed.MediaID); findErr == nil { if media, findErr := uc.FindMedia(ctx, completed.MediaID); findErr == nil {
copy := &MediaFile{Name: name, URL: media.URL, Tag: strings.TrimPrefix(filepath.Ext(name), "."), Key: media.Key, Size: media.Size, MD5: media.MD5, Mime: media.Mime, UserID: userID} copy := &MediaFile{Name: name, CategoryID: media.CategoryID, URL: media.URL, Tag: strings.TrimPrefix(filepath.Ext(name), "."), Key: media.Key, Size: media.Size, MD5: media.MD5, Mime: media.Mime, UserID: userID}
if strings.TrimSpace(copy.Mime) == "" { if strings.TrimSpace(copy.Mime) == "" {
copy.Mime = stdmime.TypeByExtension(strings.ToLower(filepath.Ext(name))) copy.Mime = stdmime.TypeByExtension(strings.ToLower(filepath.Ext(name)))
if copy.Mime == "" { if copy.Mime == "" {
@ -294,8 +294,10 @@ func (uc *MediaUsecase) CleanupStale(ctx context.Context, ttlHours int) error {
// scheduler. Keep the endpoint-independent background behavior compatible. // scheduler. Keep the endpoint-independent background behavior compatible.
ids, _ := uc.StaleUploadSessionIDs(ctx, time.Now().Add(-time.Duration(ttlHours)*time.Hour)) ids, _ := uc.StaleUploadSessionIDs(ctx, time.Now().Add(-time.Duration(ttlHours)*time.Hour))
for _, id := range ids { for _, id := range ids {
if err := uc.files.DeletePrefix(ctx, uc.chunkPrefix(id)); err != nil {
continue
}
_ = uc.DeleteUploadData(ctx, id) _ = uc.DeleteUploadData(ctx, id)
_ = uc.files.DeletePrefix(ctx, uc.chunkPrefix(id))
} }
return nil return nil
} }

View File

@ -85,7 +85,7 @@ func NewTaskUsecaseWithRegistry(repo TaskRepo, methods TaskMethodRegistry) *Task
return &TaskUsecase{TaskRepo: repo, methods: methods} return &TaskUsecase{TaskRepo: repo, methods: methods}
} }
func (uc *TaskUsecase) RegisteredMethods() []TaskMethod { return uc.methods.List() } func (uc *TaskUsecase) RegisteredMethods() []platformtask.Method { return uc.methods.List() }
func (uc *TaskUsecase) Validate(value *TimedTask) error { func (uc *TaskUsecase) Validate(value *TimedTask) error {
if value.Name == "" { if value.Name == "" {
@ -165,7 +165,7 @@ func NewTaskApplicationUsecase(tasks *TaskUsecase, runtime TaskRuntime) *TaskApp
return &TaskApplicationUsecase{tasks: tasks, runtime: runtime} return &TaskApplicationUsecase{tasks: tasks, runtime: runtime}
} }
func (uc *TaskApplicationUsecase) RegisteredMethods() []TaskMethod { func (uc *TaskApplicationUsecase) RegisteredMethods() []platformtask.Method {
return uc.tasks.RegisteredMethods() return uc.tasks.RegisteredMethods()
} }

View File

@ -2,10 +2,7 @@ package task
import platformtask "kra/pkg/task" import platformtask "kra/pkg/task"
type TaskMethodFunc = platformtask.MethodFunc
type TaskMethod = platformtask.Method
type TaskMethodRegistry interface { type TaskMethodRegistry interface {
Lookup(string) (TaskMethodFunc, bool) Lookup(string) (platformtask.MethodFunc, bool)
List() []TaskMethod List() []platformtask.Method
} }

View File

@ -1,5 +1,7 @@
package config package config
import "strings"
// MaskedSecret is the placeholder written in place of a stored secret whenever a // MaskedSecret is the placeholder written in place of a stored secret whenever a
// configuration payload leaves the process. Every layer that masks or preserves // configuration payload leaves the process. Every layer that masks or preserves
// configuration secrets shares this sentinel so a masked read round-trips into a // configuration secrets shares this sentinel so a masked read round-trips into a
@ -8,7 +10,9 @@ const MaskedSecret = "******"
// IsMaskedSecret reports whether an inbound value carries no new secret, either // IsMaskedSecret reports whether an inbound value carries no new secret, either
// because it was omitted or because it is the mask handed out on read. // because it was omitted or because it is the mask handed out on read.
func IsMaskedSecret(value string) bool { return value == "" || value == MaskedSecret } func IsMaskedSecret(value string) bool {
return strings.TrimSpace(value) == "" || strings.TrimSpace(value) == MaskedSecret
}
// ObjectStores lists every object-store block of a storage configuration so // ObjectStores lists every object-store block of a storage configuration so
// callers iterate the providers instead of repeating the list. // callers iterate the providers instead of repeating the list.

View File

@ -4,6 +4,7 @@ import (
"database/sql" "database/sql"
"errors" "errors"
"kra/internal/biz/system" "kra/internal/biz/system"
datasystem "kra/internal/data/system"
"log/slog" "log/slog"
"reflect" "reflect"
"strings" "strings"
@ -13,6 +14,10 @@ import (
"gorm.io/gorm/schema" "gorm.io/gorm/schema"
) )
// dataAccessLogPO is the infrastructure-side write model used by GORM
// callbacks. The system module owns the query repository for the same table.
type dataAccessLogPO = datasystem.DataAccessLogPO
// registerDataScopeCallbacks installs the global GORM data-scope engine. // registerDataScopeCallbacks installs the global GORM data-scope engine.
// System tables are deliberately excluded: their access is controlled by // System tables are deliberately excluded: their access is controlled by
// Casbin, while ownership columns on business tables are row-level scope. // Casbin, while ownership columns on business tables are row-level scope.

View File

@ -1,9 +0,0 @@
package data
import (
datasystem "kra/internal/data/system"
)
// dataAccessLogPO is the infrastructure-side write model used by GORM
// callbacks. The system module owns the query repository for the same table.
type dataAccessLogPO = datasystem.DataAccessLogPO

View File

@ -90,11 +90,17 @@ func (r *mediaRepo) FailUploadSession(ctx context.Context, id uint) error {
return r.data.DB().WithContext(ctx).Model(&uploadSessionPO{}).Where("id = ?", id).Update("status", "failed").Error return r.data.DB().WithContext(ctx).Model(&uploadSessionPO{}).Where("id = ?", id).Update("status", "failed").Error
} }
func (r *mediaRepo) CompleteUploadSession(ctx context.Context, id uint, key string, mediaID uint) error { func (r *mediaRepo) CompleteUploadSession(ctx context.Context, id uint, key string, mediaID uint) error {
return r.data.DB().WithContext(ctx).Model(&uploadSessionPO{}).Where("id = ?", id).Updates(map[string]any{"status": "completed", "storage_key": key, "media_id": mediaID}).Error result := r.data.DB().WithContext(ctx).Model(&uploadSessionPO{}).Where("id = ? AND status = ?", id, "merging").Updates(map[string]any{"status": "completed", "storage_key": key, "media_id": mediaID})
if result.Error != nil {
return result.Error
}
if result.RowsAffected != 1 {
return system.ErrUploadSessionNotFound
}
return nil
} }
func (r *mediaRepo) DeleteUploadSession(ctx context.Context, id uint) error { func (r *mediaRepo) DeleteUploadSession(ctx context.Context, id uint) error {
// The compatible flow uses GORM's normal Delete here, retaining the soft-deleted session // Upload sessions are staging records; cleanup physically removes them and their chunks.
// for audit/recovery rather than physically removing it.
return r.data.DB().WithContext(ctx).Unscoped().Delete(&uploadSessionPO{}, id).Error return r.data.DB().WithContext(ctx).Unscoped().Delete(&uploadSessionPO{}, id).Error
} }
func (r *mediaRepo) UpsertChunk(ctx context.Context, uploadID uint, v *system.UploadChunk) error { func (r *mediaRepo) UpsertChunk(ctx context.Context, uploadID uint, v *system.UploadChunk) error {

View File

@ -1,13 +1,5 @@
package task package task
import ( import "github.com/google/wire"
dataprovider "kra/internal/data/provider"
"github.com/google/wire"
)
// Provider is the minimal database seam required by the task tables.
// Task persistence must not depend on the full data runtime or system repos.
type Provider = dataprovider.Database
var ProviderSet = wire.NewSet(NewTaskRepo) var ProviderSet = wire.NewSet(NewTaskRepo)

View File

@ -3,6 +3,7 @@ package task
import ( import (
"context" "context"
taskbiz "kra/internal/biz/task" taskbiz "kra/internal/biz/task"
dataprovider "kra/internal/data/provider"
"kra/pkg/database/gormkit" "kra/pkg/database/gormkit"
"kra/pkg/database/pagination" "kra/pkg/database/pagination"
"time" "time"
@ -44,9 +45,9 @@ type taskLogPO struct {
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 dataprovider.Database }
func NewTaskRepo(data Provider) taskbiz.TaskRepo { return &taskRepo{data: data} } func NewTaskRepo(data dataprovider.Database) 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}
} }

View File

@ -56,8 +56,8 @@ var routes = map[string]routeValue{
"DELETE /sysExportTemplate/deleteSysExportTemplateByIds": {group: "导出模板", description: "批量删除导出模板", audit: true}, "DELETE /sysExportTemplate/deleteSysExportTemplateByIds": {group: "导出模板", description: "批量删除导出模板", audit: true},
"DELETE /sysLoginLog/deleteLoginLog": {group: "登录日志", description: "删除登录日志", audit: true}, "DELETE /sysLoginLog/deleteLoginLog": {group: "登录日志", description: "删除登录日志", audit: true},
"DELETE /sysLoginLog/deleteLoginLogByIds": {group: "登录日志", description: "批量删除登录日志", audit: true}, "DELETE /sysLoginLog/deleteLoginLogByIds": {group: "登录日志", description: "批量删除登录日志", audit: true},
"DELETE /sysOperationRecord/deleteSysOperationRecord": {group: "操作记录", description: "删除操作记录"}, "DELETE /sysOperationRecord/deleteSysOperationRecord": {group: "操作记录", description: "删除操作记录", audit: true},
"DELETE /sysOperationRecord/deleteSysOperationRecordByIds": {group: "操作记录", description: "批量删除操作历史"}, "DELETE /sysOperationRecord/deleteSysOperationRecordByIds": {group: "操作记录", description: "批量删除操作历史", audit: true},
"DELETE /sysParams/deleteSysParams": {group: "参数管理", description: "删除参数", audit: true}, "DELETE /sysParams/deleteSysParams": {group: "参数管理", description: "删除参数", audit: true},
"DELETE /sysParams/deleteSysParamsByIds": {group: "参数管理", description: "批量删除参数", audit: true}, "DELETE /sysParams/deleteSysParamsByIds": {group: "参数管理", description: "批量删除参数", audit: true},
"DELETE /sysVersion/deleteSysVersion": {group: "版本控制", description: "删除版本", audit: true}, "DELETE /sysVersion/deleteSysVersion": {group: "版本控制", description: "删除版本", audit: true},
@ -135,7 +135,7 @@ var routes = map[string]routeValue{
"POST /api/setApiRoles": {group: "api", description: "全量覆盖API关联角色列表", audit: true}, "POST /api/setApiRoles": {group: "api", description: "全量覆盖API关联角色列表", audit: true},
"POST /api/updateApi": {group: "api", description: "更新Api", audit: true}, "POST /api/updateApi": {group: "api", description: "更新Api", audit: true},
"POST /attachmentCategory/addCategory": {group: "媒体库分类", description: "添加/编辑分类"}, "POST /attachmentCategory/addCategory": {group: "媒体库分类", description: "添加/编辑分类"},
"POST /attachmentCategory/deleteCategory": {group: "媒体库分类", description: "删除分类"}, "POST /attachmentCategory/deleteCategory": {group: "媒体库分类", description: "删除分类", audit: true},
"POST /authority/copyAuthority": {group: "角色", description: "拷贝角色", audit: true}, "POST /authority/copyAuthority": {group: "角色", description: "拷贝角色", audit: true},
"POST /authority/createAuthority": {group: "角色", description: "创建角色", audit: true}, "POST /authority/createAuthority": {group: "角色", description: "创建角色", audit: true},
"POST /authority/deleteAuthority": {group: "角色", description: "删除角色", audit: true}, "POST /authority/deleteAuthority": {group: "角色", description: "删除角色", audit: true},

View File

@ -1,8 +0,0 @@
package dto
type LoginResponse struct {
User *UserResponse `json:"user"`
Token string `json:"token"`
ExpiresAt int64 `json:"expiresAt"`
NeedChangePassword bool `json:"needChangePassword"`
}

View File

@ -9,6 +9,13 @@ type LoginRequest struct {
CaptchaID string `json:"captchaId"` CaptchaID string `json:"captchaId"`
} }
type LoginResponse struct {
User *UserResponse `json:"user"`
Token string `json:"token"`
ExpiresAt int64 `json:"expiresAt"`
NeedChangePassword bool `json:"needChangePassword"`
}
type Route struct { type Route struct {
Path string Path string
Method string Method string

View File

@ -18,6 +18,7 @@ import (
"unicode/utf8" "unicode/utf8"
taskbiz "kra/internal/biz/task" taskbiz "kra/internal/biz/task"
platformtask "kra/pkg/task"
) )
type TaskExecutor struct { type TaskExecutor struct {
@ -199,7 +200,7 @@ func (e *TaskExecutor) runMethod(ctx context.Context, task *taskbiz.TimedTask) e
if ctx == nil { if ctx == nil {
ctx = context.Background() ctx = context.Background()
} }
var method taskbiz.TaskMethodFunc var method platformtask.MethodFunc
var ok bool var ok bool
if e.methods != nil { if e.methods != nil {
method, ok = e.methods.Lookup(task.MethodName) method, ok = e.methods.Lookup(task.MethodName)