优化结构
This commit is contained in:
@@ -3,9 +3,9 @@ package system
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"kra/internal/biz/system"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/pkg/database/gormkit"
|
||||
"kra/pkg/database/pagination"
|
||||
|
||||
@@ -27,17 +27,17 @@ func (announcementPO) TableName() string { return "sys_announcements" }
|
||||
|
||||
type announcementRepo struct{ data Provider }
|
||||
|
||||
func NewAnnouncementRepo(data Provider) biz.AnnouncementRepo { return &announcementRepo{data: data} }
|
||||
func NewAnnouncementRepo(data Provider) system.AnnouncementRepo { return &announcementRepo{data: data} }
|
||||
|
||||
func newAnnouncement(item *biz.Announcement) announcementPO {
|
||||
func newAnnouncement(item *system.Announcement) announcementPO {
|
||||
return announcementPO{ID: item.ID, Title: item.Title, Content: item.Content, UserID: item.UserID, Attachments: gormkit.JSON(item.Attachments)}
|
||||
}
|
||||
|
||||
func announcementToBiz(item announcementPO) *biz.Announcement {
|
||||
return &biz.Announcement{ID: item.ID, CreatedAt: item.CreatedAt, UpdatedAt: item.UpdatedAt, Title: item.Title, Content: item.Content, UserID: item.UserID, Attachments: json.RawMessage(item.Attachments)}
|
||||
func announcementToBiz(item announcementPO) *system.Announcement {
|
||||
return &system.Announcement{ID: item.ID, CreatedAt: item.CreatedAt, UpdatedAt: item.UpdatedAt, Title: item.Title, Content: item.Content, UserID: item.UserID, Attachments: json.RawMessage(item.Attachments)}
|
||||
}
|
||||
|
||||
func (r *announcementRepo) Create(ctx context.Context, item *biz.Announcement) error {
|
||||
func (r *announcementRepo) Create(ctx context.Context, item *system.Announcement) error {
|
||||
po := newAnnouncement(item)
|
||||
if err := r.data.DB().WithContext(ctx).Create(&po).Error; err != nil {
|
||||
return err
|
||||
@@ -54,12 +54,12 @@ func (r *announcementRepo) DeleteByIDs(ctx context.Context, ids []string) error
|
||||
return r.data.DB().WithContext(ctx).Delete(&[]announcementPO{}, "id IN ?", ids).Error
|
||||
}
|
||||
|
||||
func (r *announcementRepo) Update(ctx context.Context, item *biz.Announcement) error {
|
||||
func (r *announcementRepo) Update(ctx context.Context, item *system.Announcement) error {
|
||||
po := newAnnouncement(item)
|
||||
return r.data.DB().WithContext(ctx).Model(&announcementPO{}).Where("id = ?", item.ID).Updates(&po).Error
|
||||
}
|
||||
|
||||
func (r *announcementRepo) Find(ctx context.Context, id string) (*biz.Announcement, error) {
|
||||
func (r *announcementRepo) Find(ctx context.Context, id string) (*system.Announcement, error) {
|
||||
var po announcementPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("id = ?", id).First(&po).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -67,7 +67,7 @@ func (r *announcementRepo) Find(ctx context.Context, id string) (*biz.Announceme
|
||||
return announcementToBiz(po), nil
|
||||
}
|
||||
|
||||
func (r *announcementRepo) List(ctx context.Context, filter biz.AnnouncementFilter) ([]*biz.Announcement, int64, error) {
|
||||
func (r *announcementRepo) List(ctx context.Context, filter system.AnnouncementFilter) ([]*system.Announcement, int64, error) {
|
||||
db := r.data.DB().WithContext(ctx).Model(&announcementPO{})
|
||||
if filter.StartCreatedAt != nil && filter.EndCreatedAt != nil {
|
||||
db = db.Where("created_at BETWEEN ? AND ?", filter.StartCreatedAt, filter.EndCreatedAt)
|
||||
@@ -83,14 +83,14 @@ func (r *announcementRepo) List(ctx context.Context, filter biz.AnnouncementFilt
|
||||
if err := db.Find(&pos).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
items := make([]*biz.Announcement, 0, len(pos))
|
||||
items := make([]*system.Announcement, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
items = append(items, announcementToBiz(po))
|
||||
}
|
||||
return items, total, nil
|
||||
}
|
||||
|
||||
func (r *announcementRepo) UserOptions(ctx context.Context) ([]biz.UserOption, error) {
|
||||
func (r *announcementRepo) UserOptions(ctx context.Context) ([]system.UserOption, error) {
|
||||
var rows []struct {
|
||||
Label string
|
||||
Value uint
|
||||
@@ -98,9 +98,9 @@ func (r *announcementRepo) UserOptions(ctx context.Context) ([]biz.UserOption, e
|
||||
// The generated data-source endpoint is best effort: return collected
|
||||
// options even when the underlying scan reports an error.
|
||||
_ = r.data.DB().WithContext(ctx).Table("sys_users").Select("nick_name AS label, id AS value").Scan(&rows).Error
|
||||
items := make([]biz.UserOption, 0, len(rows))
|
||||
items := make([]system.UserOption, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
items = append(items, biz.UserOption{Label: row.Label, Value: row.Value})
|
||||
items = append(items, system.UserOption{Label: row.Label, Value: row.Value})
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
@@ -2,16 +2,15 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz/system"
|
||||
"testing"
|
||||
|
||||
"kra/internal/biz"
|
||||
)
|
||||
|
||||
func TestAnnouncementRepositoryKeepsRawIDQuerySemantics(t *testing.T) {
|
||||
data := newTransactionTestData(t)
|
||||
repo := &announcementRepo{data: data}
|
||||
ctx := context.Background()
|
||||
item := &biz.Announcement{Title: "notice"}
|
||||
item := &system.Announcement{Title: "notice"}
|
||||
if err := repo.Create(ctx, item); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -44,7 +43,7 @@ func TestAnnouncementRepositoryPreservesSignedUserID(t *testing.T) {
|
||||
repo := &announcementRepo{data: data}
|
||||
ctx := context.Background()
|
||||
userID := -1
|
||||
item := &biz.Announcement{Title: "notice", UserID: &userID}
|
||||
item := &system.Announcement{Title: "notice", UserID: &userID}
|
||||
if err := repo.Create(ctx, item); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@ package system
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"kra/internal/biz/system"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/pkg/database/pagination"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
type apiRepo struct{ data Provider }
|
||||
|
||||
func NewAPIRepo(data Provider) biz.APIRepo { return &apiRepo{data: data} }
|
||||
func NewAPIRepo(data Provider) system.APIRepo { return &apiRepo{data: data} }
|
||||
|
||||
type apiPO struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
@@ -47,10 +47,10 @@ type authorityAPIPO struct {
|
||||
|
||||
func (authorityAPIPO) TableName() string { return "sys_authority_apis" }
|
||||
|
||||
func apiFromPO(po apiPO) *biz.API {
|
||||
return &biz.API{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Path: po.Path, Description: po.Description, APIGroup: po.APIGroup, Method: po.Method}
|
||||
func apiFromPO(po apiPO) *system.API {
|
||||
return &system.API{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Path: po.Path, Description: po.Description, APIGroup: po.APIGroup, Method: po.Method}
|
||||
}
|
||||
func (r *apiRepo) CreateAPI(ctx context.Context, v *biz.API) error {
|
||||
func (r *apiRepo) CreateAPI(ctx context.Context, v *system.API) error {
|
||||
po := apiPO{Path: v.Path, Description: v.Description, APIGroup: v.APIGroup, Method: v.Method}
|
||||
var count int64
|
||||
if err := r.data.DB().WithContext(ctx).Model(&apiPO{}).Where("path = ? AND method = ?", po.Path, po.Method).Count(&count).Error; err != nil {
|
||||
@@ -65,7 +65,7 @@ func (r *apiRepo) CreateAPI(ctx context.Context, v *biz.API) error {
|
||||
v.ID, v.CreatedAt, v.UpdatedAt, v.Method = po.ID, po.CreatedAt, po.UpdatedAt, po.Method
|
||||
return nil
|
||||
}
|
||||
func (r *apiRepo) UpdateAPI(ctx context.Context, v *biz.API) error {
|
||||
func (r *apiRepo) UpdateAPI(ctx context.Context, v *system.API) error {
|
||||
db := r.data.DB().WithContext(ctx)
|
||||
var old apiPO
|
||||
if err := db.First(&old, v.ID).Error; err != nil {
|
||||
@@ -109,18 +109,18 @@ func (r *apiRepo) DeleteAPIs(ctx context.Context, ids []uint) error {
|
||||
return tx.Delete(&apiPO{}, ids).Error
|
||||
})
|
||||
}
|
||||
func (r *apiRepo) FindAPI(ctx context.Context, id uint) (*biz.API, error) {
|
||||
func (r *apiRepo) FindAPI(ctx context.Context, id uint) (*system.API, error) {
|
||||
var po apiPO
|
||||
if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return apiFromPO(po), nil
|
||||
}
|
||||
func (r *apiRepo) ListAPIs(ctx context.Context, page, size int, q *biz.API) ([]*biz.API, int64, error) {
|
||||
func (r *apiRepo) ListAPIs(ctx context.Context, page, size int, q *system.API) ([]*system.API, int64, error) {
|
||||
db := r.data.DB().WithContext(ctx).Model(&apiPO{})
|
||||
if q != nil && q.StrictAll {
|
||||
config := r.data.Runtime().Admin()
|
||||
if actor, ok := biz.ActorFromContext(ctx); ok && config != nil && config.System != nil && config.System.UseStrictAuth {
|
||||
if actor, ok := system.ActorFromContext(ctx); ok && config != nil && config.System != nil && config.System.UseStrictAuth {
|
||||
var authority authorityPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("authority_id = ?", actor.AuthorityID).First(&authority).Error; err != nil {
|
||||
return nil, 0, err
|
||||
@@ -173,7 +173,7 @@ func (r *apiRepo) ListAPIs(ctx context.Context, page, size int, q *biz.API) ([]*
|
||||
if err := query.Find(&pos).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]*biz.API, 0, len(pos))
|
||||
out := make([]*system.API, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
out = append(out, apiFromPO(po))
|
||||
}
|
||||
|
||||
@@ -3,10 +3,9 @@ package system
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"kra/internal/biz/system"
|
||||
"strconv"
|
||||
|
||||
"kra/internal/biz"
|
||||
|
||||
"github.com/casbin/casbin/v3"
|
||||
casbinmodel "github.com/casbin/casbin/v3/model"
|
||||
"gorm.io/gorm"
|
||||
@@ -43,7 +42,7 @@ func (r *apiRepo) SetAPIRoles(ctx context.Context, path, method string, ids []ui
|
||||
return errors.New("您提交的角色ID不合法")
|
||||
}
|
||||
}
|
||||
if err := r.checkPolicyPathsAuth(ctx, []*biz.API{{Path: path, Method: method}}); err != nil {
|
||||
if err := r.checkPolicyPathsAuth(ctx, []*system.API{{Path: path, Method: method}}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -105,21 +104,21 @@ m = r.sub == p.sub && keyMatch2(r.obj, p.obj) && r.act == p.act`)
|
||||
}
|
||||
return enforcer.Enforce(subject, path, method)
|
||||
}
|
||||
func (r *apiRepo) PolicyPaths(ctx context.Context, aid uint) ([]*biz.API, error) {
|
||||
func (r *apiRepo) PolicyPaths(ctx context.Context, aid uint) ([]*system.API, error) {
|
||||
rows, err := policyRowsForAuthority(r.data.DB().WithContext(ctx), aid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out []*biz.API
|
||||
var out []*system.API
|
||||
if len(rows) > 0 {
|
||||
out = make([]*biz.API, 0, len(rows))
|
||||
out = make([]*system.API, 0, len(rows))
|
||||
}
|
||||
for _, row := range rows {
|
||||
out = append(out, &biz.API{Path: row.V1, Method: row.V2})
|
||||
out = append(out, &system.API{Path: row.V1, Method: row.V2})
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
func (r *apiRepo) SetPolicyPaths(ctx context.Context, aid uint, paths []*biz.API) error {
|
||||
func (r *apiRepo) SetPolicyPaths(ctx context.Context, aid uint, paths []*system.API) error {
|
||||
if err := (&authorityAccessRepo{data: r.data}).checkAuthorityIDAuth(ctx, aid); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -155,7 +154,7 @@ func (r *apiRepo) SetPolicyPaths(ctx context.Context, aid uint, paths []*biz.API
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *apiRepo) checkPolicyPathsAuth(ctx context.Context, paths []*biz.API) error {
|
||||
func (r *apiRepo) checkPolicyPathsAuth(ctx context.Context, paths []*system.API) error {
|
||||
actor, _, strict, err := (&authorityAccessRepo{data: r.data}).strictAuthorityAccess(ctx)
|
||||
if err != nil || !strict {
|
||||
return err
|
||||
|
||||
@@ -2,10 +2,10 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz/system"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/conf"
|
||||
)
|
||||
|
||||
@@ -33,7 +33,7 @@ func apiPolicyUintPointer(value uint) *uint { return &value }
|
||||
|
||||
func TestSetPolicyPathsStrictRootRequiresRegisteredAPI(t *testing.T) {
|
||||
data := newPolicyTestData(t)
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{AuthorityID: 888})
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{AuthorityID: 888})
|
||||
if err := data.gormDB.WithContext(ctx).Create(&[]authorityPO{
|
||||
{AuthorityID: 888, ParentID: apiPolicyUintPointer(0)},
|
||||
{AuthorityID: 999, ParentID: apiPolicyUintPointer(888)},
|
||||
@@ -44,18 +44,18 @@ func TestSetPolicyPathsStrictRootRequiresRegisteredAPI(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err := (&apiRepo{data: data}).SetPolicyPaths(ctx, 999, []*biz.API{{Path: "/unknown", Method: "POST"}})
|
||||
err := (&apiRepo{data: data}).SetPolicyPaths(ctx, 999, []*system.API{{Path: "/unknown", Method: "POST"}})
|
||||
if err == nil || !strings.Contains(err.Error(), "存在api不在权限列表中") {
|
||||
t.Fatalf("unregistered root assignment error = %v", err)
|
||||
}
|
||||
if err := (&apiRepo{data: data}).SetPolicyPaths(ctx, 999, []*biz.API{{Path: "/known", Method: "POST"}}); err != nil {
|
||||
if err := (&apiRepo{data: data}).SetPolicyPaths(ctx, 999, []*system.API{{Path: "/known", Method: "POST"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetPolicyPathsStrictChildRejectsStalePolicy(t *testing.T) {
|
||||
data := newPolicyTestData(t)
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{AuthorityID: 1001})
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{AuthorityID: 1001})
|
||||
if err := data.gormDB.WithContext(ctx).Create(&[]authorityPO{
|
||||
{AuthorityID: 888, ParentID: apiPolicyUintPointer(0)},
|
||||
{AuthorityID: 1001, ParentID: apiPolicyUintPointer(888)},
|
||||
@@ -73,18 +73,18 @@ func TestSetPolicyPathsStrictChildRejectsStalePolicy(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err := (&apiRepo{data: data}).SetPolicyPaths(ctx, 1002, []*biz.API{{Path: "/stale", Method: "POST"}})
|
||||
err := (&apiRepo{data: data}).SetPolicyPaths(ctx, 1002, []*system.API{{Path: "/stale", Method: "POST"}})
|
||||
if err == nil || !strings.Contains(err.Error(), "存在api不在权限列表中") {
|
||||
t.Fatalf("stale child assignment error = %v", err)
|
||||
}
|
||||
if err := (&apiRepo{data: data}).SetPolicyPaths(ctx, 1002, []*biz.API{{Path: "/known", Method: "POST"}}); err != nil {
|
||||
if err := (&apiRepo{data: data}).SetPolicyPaths(ctx, 1002, []*system.API{{Path: "/known", Method: "POST"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetPolicyPathsUsesCompatibleDedupeKey(t *testing.T) {
|
||||
data := newPolicyTestData(t)
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{AuthorityID: 888})
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{AuthorityID: 888})
|
||||
if err := data.gormDB.WithContext(ctx).Create(&[]authorityPO{
|
||||
{AuthorityID: 888, ParentID: apiPolicyUintPointer(0)},
|
||||
{AuthorityID: 999, ParentID: apiPolicyUintPointer(888)},
|
||||
@@ -98,7 +98,7 @@ func TestSetPolicyPathsUsesCompatibleDedupeKey(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := (&apiRepo{data: data}).SetPolicyPaths(ctx, 999, []*biz.API{
|
||||
if err := (&apiRepo{data: data}).SetPolicyPaths(ctx, 999, []*system.API{
|
||||
{Path: "/a", Method: "BC"},
|
||||
{Path: "/aB", Method: "C"},
|
||||
}); err != nil {
|
||||
@@ -148,7 +148,7 @@ func TestSetAPIRolesStrictOnlyChangesManagedAuthorities(t *testing.T) {
|
||||
}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{AuthorityID: actorID})
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{AuthorityID: actorID})
|
||||
repo := &apiRepo{data: data}
|
||||
if err := repo.SetAPIRoles(ctx, "/known", "POST", []uint{siblingID}); err == nil {
|
||||
t.Fatal("SetAPIRoles() accepted an out-of-scope authority")
|
||||
|
||||
@@ -2,20 +2,19 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/biz/system"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func (r *apiRepo) IgnoredAPIs(ctx context.Context) ([]*biz.API, error) {
|
||||
func (r *apiRepo) IgnoredAPIs(ctx context.Context) ([]*system.API, error) {
|
||||
var pos []ignoredAPIPO
|
||||
if err := r.data.DB().WithContext(ctx).Find(&pos).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]*biz.API, 0, len(pos))
|
||||
out := make([]*system.API, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
out = append(out, &biz.API{Path: po.Path, Method: po.Method})
|
||||
out = append(out, &system.API{Path: po.Path, Method: po.Method})
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
@@ -28,7 +27,7 @@ func (r *apiRepo) SetAPIIgnored(ctx context.Context, path, method string, ignore
|
||||
}
|
||||
return r.data.DB().WithContext(ctx).Unscoped().Where("path = ? AND method = ?", po.Path, po.Method).Delete(&ignoredAPIPO{}).Error
|
||||
}
|
||||
func (r *apiRepo) ApplyAPISync(ctx context.Context, added, deleted []*biz.API) error {
|
||||
func (r *apiRepo) ApplyAPISync(ctx context.Context, added, deleted []*system.API) error {
|
||||
return r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if len(added) > 0 {
|
||||
pos := make([]apiPO, 0, len(added))
|
||||
|
||||
@@ -3,9 +3,9 @@ package system
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"kra/internal/biz/system"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/pkg/database/pagination"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
type apiTokenRepo struct{ data Provider }
|
||||
|
||||
func NewAPITokenRepo(data Provider) biz.APITokenRepo { return &apiTokenRepo{data: data} }
|
||||
func NewAPITokenRepo(data Provider) system.APITokenRepo { return &apiTokenRepo{data: data} }
|
||||
|
||||
type apiTokenPO struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
@@ -40,7 +40,7 @@ type jwtBlacklistPO struct {
|
||||
|
||||
func (jwtBlacklistPO) TableName() string { return "jwt_blacklists" }
|
||||
|
||||
func (r *apiTokenRepo) UserHasAuthority(ctx context.Context, userID, authorityID uint) (*biz.User, bool, error) {
|
||||
func (r *apiTokenRepo) UserHasAuthority(ctx context.Context, userID, authorityID uint) (*system.User, bool, error) {
|
||||
var po userPO
|
||||
if err := r.data.DB().WithContext(ctx).First(&po, userID).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
@@ -59,7 +59,7 @@ func (r *apiTokenRepo) UserHasAuthority(ctx context.Context, userID, authorityID
|
||||
user, err := (&userRepo{data: r.data}).loadUser(ctx, &po)
|
||||
return user, count > 0 || po.AuthorityID == authorityID, err
|
||||
}
|
||||
func (r *apiTokenRepo) CreateAPIToken(ctx context.Context, v *biz.APIToken) error {
|
||||
func (r *apiTokenRepo) CreateAPIToken(ctx context.Context, v *system.APIToken) error {
|
||||
po := apiTokenPO{UserID: v.UserID, AuthorityID: v.AuthorityID, Token: v.Token, Status: v.Status, ExpiresAt: v.ExpiresAt, Remark: v.Remark}
|
||||
if err := r.data.DB().WithContext(ctx).Create(&po).Error; err != nil {
|
||||
return err
|
||||
@@ -68,7 +68,7 @@ func (r *apiTokenRepo) CreateAPIToken(ctx context.Context, v *biz.APIToken) erro
|
||||
v.CreatedAt = po.CreatedAt
|
||||
return nil
|
||||
}
|
||||
func (r *apiTokenRepo) ListAPITokens(ctx context.Context, page, size int, userID uint, status *bool) ([]*biz.APIToken, int64, error) {
|
||||
func (r *apiTokenRepo) ListAPITokens(ctx context.Context, page, size int, userID uint, status *bool) ([]*system.APIToken, int64, error) {
|
||||
db := r.data.DB().WithContext(ctx).Model(&apiTokenPO{})
|
||||
if userID != 0 {
|
||||
db = db.Where("user_id = ?", userID)
|
||||
@@ -94,13 +94,13 @@ func (r *apiTokenRepo) ListAPITokens(ctx context.Context, page, size int, userID
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
users := make(map[uint]*biz.User, len(userPOs))
|
||||
users := make(map[uint]*system.User, len(userPOs))
|
||||
for i := range userPOs {
|
||||
users[userPOs[i].ID] = baseBizUser(&userPOs[i])
|
||||
}
|
||||
out := make([]*biz.APIToken, 0, len(pos))
|
||||
out := make([]*system.APIToken, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
v := &biz.APIToken{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, UserID: po.UserID, AuthorityID: po.AuthorityID, Token: po.Token, Status: po.Status, ExpiresAt: po.ExpiresAt, Remark: po.Remark, User: users[po.UserID]}
|
||||
v := &system.APIToken{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, UserID: po.UserID, AuthorityID: po.AuthorityID, Token: po.Token, Status: po.Status, ExpiresAt: po.ExpiresAt, Remark: po.Remark, User: users[po.UserID]}
|
||||
out = append(out, v)
|
||||
}
|
||||
return out, total, nil
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
package system
|
||||
|
||||
import "kra/internal/biz"
|
||||
import (
|
||||
"kra/internal/biz/system"
|
||||
)
|
||||
|
||||
type auditQueryRepo struct{ data Provider }
|
||||
type auditRecorderRepo struct{ data Provider }
|
||||
type logFileRepo struct{ data Provider }
|
||||
|
||||
func NewAuditRepo(data Provider) biz.AuditQueryRepo { return &auditQueryRepo{data: data} }
|
||||
func NewAuditRepo(data Provider) system.AuditQueryRepo { return &auditQueryRepo{data: data} }
|
||||
|
||||
func NewAuditRecorderRepo(data Provider) biz.AuditRecordRepo { return &auditRecorderRepo{data: data} }
|
||||
func NewAuditRecorderRepo(data Provider) system.AuditRecordRepo {
|
||||
return &auditRecorderRepo{data: data}
|
||||
}
|
||||
|
||||
func NewLogFileRepo(data Provider) biz.LogFileRepo { return &logFileRepo{data: data} }
|
||||
func NewLogFileRepo(data Provider) system.LogFileRepo { return &logFileRepo{data: data} }
|
||||
|
||||
@@ -3,8 +3,7 @@ package system
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/biz/system"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -13,7 +12,7 @@ var errInvalidDataScope = errors.New("数据权限范围不合法")
|
||||
|
||||
type authorityAccessRepo struct{ data Provider }
|
||||
|
||||
func NewAuthorityAccessRepo(data Provider) biz.AuthorityAccessRepo {
|
||||
func NewAuthorityAccessRepo(data Provider) system.AuthorityAccessRepo {
|
||||
return &authorityAccessRepo{data: data}
|
||||
}
|
||||
|
||||
@@ -58,18 +57,18 @@ func (r *authorityAccessRepo) strictAuthorityIDs(ctx context.Context, actorID ui
|
||||
return allowed, nil
|
||||
}
|
||||
|
||||
func (r *authorityAccessRepo) strictAuthorityAccess(ctx context.Context) (biz.Actor, map[uint]bool, bool, error) {
|
||||
func (r *authorityAccessRepo) strictAuthorityAccess(ctx context.Context) (system.Actor, map[uint]bool, bool, error) {
|
||||
config := r.data.Runtime().Admin()
|
||||
if config == nil || config.System == nil || !config.System.UseStrictAuth {
|
||||
return biz.Actor{}, nil, false, nil
|
||||
return system.Actor{}, nil, false, nil
|
||||
}
|
||||
actor, ok := biz.ActorFromContext(ctx)
|
||||
actor, ok := system.ActorFromContext(ctx)
|
||||
if !ok {
|
||||
return biz.Actor{}, nil, true, errors.New("您提交的角色ID不合法")
|
||||
return system.Actor{}, nil, true, errors.New("您提交的角色ID不合法")
|
||||
}
|
||||
allowed, err := r.strictAuthorityIDs(ctx, actor.AuthorityID)
|
||||
if err != nil {
|
||||
return biz.Actor{}, nil, true, err
|
||||
return system.Actor{}, nil, true, err
|
||||
}
|
||||
return actor, allowed, true, nil
|
||||
}
|
||||
@@ -91,7 +90,7 @@ func (r *authorityAccessRepo) checkAuthorityIDAuth(ctx context.Context, targetID
|
||||
return r.checkAuthorityIDsAuth(ctx, []uint{targetID})
|
||||
}
|
||||
|
||||
func managedAuthorityParent(actor biz.Actor, allowed map[uint]bool, targetID uint, parentID *uint, creating bool) (*uint, error) {
|
||||
func managedAuthorityParent(actor system.Actor, allowed map[uint]bool, targetID uint, parentID *uint, creating bool) (*uint, error) {
|
||||
if creating && (parentID == nil || *parentID == 0) {
|
||||
value := actor.AuthorityID
|
||||
return &value, nil
|
||||
@@ -131,7 +130,7 @@ func (r *authorityAccessRepo) ensureAuthorityParentAcyclic(ctx context.Context,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *authorityAccessRepo) CreateAuthority(ctx context.Context, value *biz.Authority) error {
|
||||
func (r *authorityAccessRepo) CreateAuthority(ctx context.Context, value *system.Authority) error {
|
||||
if value.DataScope == 0 {
|
||||
value.DataScope = 1
|
||||
} else if value.DataScope < 1 || value.DataScope > 5 {
|
||||
@@ -168,7 +167,7 @@ func (r *authorityAccessRepo) CreateAuthority(ctx context.Context, value *biz.Au
|
||||
}
|
||||
value.CreatedAt, value.UpdatedAt = po.CreatedAt, po.UpdatedAt
|
||||
value.DataScope, value.DefaultRouter = po.DataScope, po.DefaultRouter
|
||||
value.Menus = []*biz.Menu{{ID: 1, Path: "dashboard", Name: "dashboard", Component: "view/dashboard/index.vue", Sort: 1, Title: "仪表盘", Icon: "setting"}}
|
||||
value.Menus = []*system.Menu{{ID: 1, Path: "dashboard", Name: "dashboard", Component: "view/dashboard/index.vue", Sort: 1, Title: "仪表盘", Icon: "setting"}}
|
||||
var dashboard menuPO
|
||||
if err := tx.Where("name = ?", "dashboard").First(&dashboard).Error; err == nil {
|
||||
grantDashboard := true
|
||||
@@ -209,7 +208,7 @@ func (r *authorityAccessRepo) CreateAuthority(ctx context.Context, value *biz.Au
|
||||
return nil
|
||||
})
|
||||
}
|
||||
func (r *authorityAccessRepo) CopyAuthority(ctx context.Context, sourceID uint, value *biz.Authority) error {
|
||||
func (r *authorityAccessRepo) CopyAuthority(ctx context.Context, sourceID uint, value *system.Authority) error {
|
||||
if value.DataScope == 0 {
|
||||
value.DataScope = 1
|
||||
} else if value.DataScope < 1 || value.DataScope > 5 {
|
||||
@@ -283,7 +282,7 @@ func (r *authorityAccessRepo) CopyAuthority(ctx context.Context, sourceID uint,
|
||||
if err := tx.Where("id IN ?", menuIDs).Order("sort").Find(&copiedMenus).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
value.Menus = make([]*biz.Menu, 0, len(copiedMenus))
|
||||
value.Menus = make([]*system.Menu, 0, len(copiedMenus))
|
||||
for _, menu := range copiedMenus {
|
||||
value.Menus = append(value.Menus, menuFromPO(menu))
|
||||
}
|
||||
@@ -362,7 +361,7 @@ func (r *authorityAccessRepo) copyPolicyAllowed(ctx context.Context, tx *gorm.DB
|
||||
}
|
||||
return policyExists(tx.WithContext(ctx), actorID, path, method)
|
||||
}
|
||||
func (r *authorityAccessRepo) UpdateAuthority(ctx context.Context, value *biz.Authority) error {
|
||||
func (r *authorityAccessRepo) UpdateAuthority(ctx context.Context, value *system.Authority) error {
|
||||
if err := r.checkAuthorityIDAuth(ctx, value.AuthorityID); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -464,11 +463,11 @@ func (r *authorityAccessRepo) DeleteAuthority(ctx context.Context, id uint) erro
|
||||
return tx.Unscoped().Delete(&authorityPO{}, "authority_id = ?", id).Error
|
||||
})
|
||||
}
|
||||
func (r *authorityAccessRepo) ListAuthorities(ctx context.Context) ([]*biz.Authority, error) {
|
||||
func (r *authorityAccessRepo) ListAuthorities(ctx context.Context) ([]*system.Authority, error) {
|
||||
db := r.data.DB().WithContext(ctx)
|
||||
var allowed map[uint]bool
|
||||
config := r.data.Runtime().Admin()
|
||||
if actor, ok := biz.ActorFromContext(ctx); ok {
|
||||
if actor, ok := system.ActorFromContext(ctx); ok {
|
||||
// The current authority is loaded even when strict mode is disabled;
|
||||
// an invalid token authority therefore fails the list request instead of
|
||||
// exposing the complete role tree.
|
||||
@@ -488,7 +487,7 @@ func (r *authorityAccessRepo) ListAuthorities(ctx context.Context) ([]*biz.Autho
|
||||
if err := db.Find(&pos).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]*biz.Authority, 0, len(pos))
|
||||
out := make([]*system.Authority, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
if allowed != nil && !allowed[po.AuthorityID] {
|
||||
continue
|
||||
@@ -752,8 +751,8 @@ func (r *authorityAccessRepo) DataScopeDepartmentIDs(ctx context.Context, id uin
|
||||
err := r.data.DB().WithContext(ctx).Model(&authorityDepartmentPO{}).Where("sys_authority_authority_id = ?", id).Pluck("sys_department_id", &ids).Error
|
||||
return ids, err
|
||||
}
|
||||
func (r *authorityAccessRepo) ResolveDataScope(ctx context.Context, authorityID, userID uint) (biz.DataScope, error) {
|
||||
identity := biz.DataScope{UserID: userID, AuthorityID: authorityID}
|
||||
func (r *authorityAccessRepo) ResolveDataScope(ctx context.Context, authorityID, userID uint) (system.DataScope, error) {
|
||||
identity := system.DataScope{UserID: userID, AuthorityID: authorityID}
|
||||
var user userPO
|
||||
if err := r.data.DB().WithContext(ctx).Select("id", "dept_id").First(&user, userID).Error; err != nil {
|
||||
return identity, err
|
||||
|
||||
@@ -3,9 +3,9 @@ package system
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"kra/internal/biz/system"
|
||||
"testing"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/conf"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -44,8 +44,8 @@ func TestCopyAuthorityStrictPolicyValidationMatchesAdministrationContract(t *tes
|
||||
}
|
||||
|
||||
repo := &authorityAccessRepo{data: data}
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{AuthorityID: rootID})
|
||||
created := &biz.Authority{AuthorityID: 910, AuthorityName: "copy", ParentID: &rootID}
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{AuthorityID: rootID})
|
||||
created := &system.Authority{AuthorityID: 910, AuthorityName: "copy", ParentID: &rootID}
|
||||
if err := repo.CopyAuthority(ctx, 900, created); err != nil {
|
||||
t.Fatalf("root copy of a registered API failed: %v", err)
|
||||
}
|
||||
@@ -53,7 +53,7 @@ func TestCopyAuthorityStrictPolicyValidationMatchesAdministrationContract(t *tes
|
||||
t.Fatalf("copied policy exists = %v, err = %v", exists, err)
|
||||
}
|
||||
|
||||
staleCopy := &biz.Authority{AuthorityID: 911, AuthorityName: "stale-copy", ParentID: &rootID}
|
||||
staleCopy := &system.Authority{AuthorityID: 911, AuthorityName: "stale-copy", ParentID: &rootID}
|
||||
if err := repo.CopyAuthority(ctx, 901, staleCopy); err == nil || err.Error() != "存在api不在权限列表中" {
|
||||
t.Fatalf("stale API copy error = %v", err)
|
||||
}
|
||||
@@ -78,8 +78,8 @@ func TestCopyAuthorityDuplicateIDWinsOverStrictParentValidation(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
invalidParent := uint(999999)
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{AuthorityID: rootID})
|
||||
err := (&authorityAccessRepo{data: data}).CopyAuthority(ctx, 0, &biz.Authority{AuthorityID: 920, AuthorityName: "duplicate", ParentID: &invalidParent})
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{AuthorityID: rootID})
|
||||
err := (&authorityAccessRepo{data: data}).CopyAuthority(ctx, 0, &system.Authority{AuthorityID: 920, AuthorityName: "duplicate", ParentID: &invalidParent})
|
||||
if err == nil || err.Error() != "存在相同角色id" {
|
||||
t.Fatalf("duplicate copy error = %v", err)
|
||||
}
|
||||
@@ -87,7 +87,7 @@ func TestCopyAuthorityDuplicateIDWinsOverStrictParentValidation(t *testing.T) {
|
||||
|
||||
func TestListAuthoritiesRequiresCurrentAuthorityOutsideStrictMode(t *testing.T) {
|
||||
data := newTransactionTestData(t)
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{AuthorityID: 999999})
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{AuthorityID: 999999})
|
||||
if _, err := (&authorityAccessRepo{data: data}).ListAuthorities(ctx); !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
t.Fatalf("missing current authority error = %v", err)
|
||||
}
|
||||
@@ -198,7 +198,7 @@ func TestSetAuthorityUsersStrictRejectsUsersOutsideManagedRoles(t *testing.T) {
|
||||
if err := db.Create(&users).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{AuthorityID: actorID})
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{AuthorityID: actorID})
|
||||
repo := &authorityAccessRepo{data: data}
|
||||
if err := repo.SetAuthorityUsers(ctx, childID, []uint{users[1].ID}); err == nil {
|
||||
t.Fatal("SetAuthorityUsers() accepted a user outside the managed role tree")
|
||||
@@ -231,7 +231,7 @@ func TestSetAuthorityUsersStrictRejectsMixedRoleUserAlreadyLinked(t *testing.T)
|
||||
}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{AuthorityID: actorID})
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{AuthorityID: actorID})
|
||||
if err := (&authorityAccessRepo{data: data}).SetAuthorityUsers(ctx, childID, nil); err == nil {
|
||||
t.Fatal("SetAuthorityUsers() modified a linked user that also has an out-of-scope role")
|
||||
}
|
||||
@@ -257,8 +257,8 @@ func TestUpdateAuthorityStrictRejectsHierarchyCycle(t *testing.T) {
|
||||
}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{AuthorityID: actorID})
|
||||
err := (&authorityAccessRepo{data: data}).UpdateAuthority(ctx, &biz.Authority{AuthorityID: targetID, AuthorityName: "target", ParentID: &childID})
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{AuthorityID: actorID})
|
||||
err := (&authorityAccessRepo{data: data}).UpdateAuthority(ctx, &system.Authority{AuthorityID: targetID, AuthorityName: "target", ParentID: &childID})
|
||||
if err == nil {
|
||||
t.Fatal("UpdateAuthority() accepted a parent that forms a cycle")
|
||||
}
|
||||
@@ -283,7 +283,7 @@ func TestSetDataScopeValidatesScopeAndStrictDepartments(t *testing.T) {
|
||||
}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{AuthorityID: actorID})
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{AuthorityID: actorID})
|
||||
repo := &authorityAccessRepo{data: data}
|
||||
if err := repo.SetDataScope(ctx, childID, 0, nil); !errors.Is(err, errInvalidDataScope) {
|
||||
t.Fatalf("SetDataScope() invalid scope error = %v", err)
|
||||
@@ -323,7 +323,7 @@ func TestSetDataScopeStrictRejectsDepartmentsOutsideActorScope(t *testing.T) {
|
||||
if err := db.Create(&authorityDepartmentPO{AuthorityID: actorID, DepartmentID: departments[0].ID}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{UserID: actorUser.ID, AuthorityID: actorID})
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{UserID: actorUser.ID, AuthorityID: actorID})
|
||||
repo := &authorityAccessRepo{data: data}
|
||||
if err := repo.SetDataScope(ctx, childID, 5, []uint{departments[1].ID}); err == nil {
|
||||
t.Fatal("SetDataScope() accepted a department outside the actor's data scope")
|
||||
@@ -350,31 +350,31 @@ func TestStrictDataScopeGrantRejectsBroaderChildScopes(t *testing.T) {
|
||||
if err := db.Create(&actorUser).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{UserID: actorUser.ID, AuthorityID: actorID})
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{UserID: actorUser.ID, AuthorityID: actorID})
|
||||
repo := &authorityAccessRepo{data: data}
|
||||
|
||||
created := &biz.Authority{AuthorityID: 1373, AuthorityName: "created", ParentID: &actorID}
|
||||
created := &system.Authority{AuthorityID: 1373, AuthorityName: "created", ParentID: &actorID}
|
||||
if err := repo.CreateAuthority(ctx, created); !errors.Is(err, errInvalidDataScope) {
|
||||
t.Fatalf("CreateAuthority() broader scope error = %v", err)
|
||||
}
|
||||
if created.DataScope != 1 {
|
||||
t.Fatalf("CreateAuthority() did not normalize zero scope before validation: %d", created.DataScope)
|
||||
}
|
||||
copied := &biz.Authority{AuthorityID: 1374, AuthorityName: "copied", ParentID: &actorID}
|
||||
copied := &system.Authority{AuthorityID: 1374, AuthorityName: "copied", ParentID: &actorID}
|
||||
if err := repo.CopyAuthority(ctx, sourceID, copied); !errors.Is(err, errInvalidDataScope) {
|
||||
t.Fatalf("CopyAuthority() broader scope error = %v", err)
|
||||
}
|
||||
if copied.DataScope != 1 {
|
||||
t.Fatalf("CopyAuthority() did not normalize zero scope before validation: %d", copied.DataScope)
|
||||
}
|
||||
if err := repo.UpdateAuthority(ctx, &biz.Authority{AuthorityID: targetID, AuthorityName: "target", ParentID: &actorID, DataScope: 2}); !errors.Is(err, errInvalidDataScope) {
|
||||
if err := repo.UpdateAuthority(ctx, &system.Authority{AuthorityID: targetID, AuthorityName: "target", ParentID: &actorID, DataScope: 2}); !errors.Is(err, errInvalidDataScope) {
|
||||
t.Fatalf("UpdateAuthority() broader scope error = %v", err)
|
||||
}
|
||||
if err := repo.SetDataScope(ctx, targetID, 1, nil); !errors.Is(err, errInvalidDataScope) {
|
||||
t.Fatalf("SetDataScope() broader scope error = %v", err)
|
||||
}
|
||||
|
||||
allowed := &biz.Authority{AuthorityID: 1375, AuthorityName: "self-only", ParentID: &actorID, DataScope: 4}
|
||||
allowed := &system.Authority{AuthorityID: 1375, AuthorityName: "self-only", ParentID: &actorID, DataScope: 4}
|
||||
if err := repo.CreateAuthority(ctx, allowed); err != nil {
|
||||
t.Fatalf("CreateAuthority() rejected a narrower scope: %v", err)
|
||||
}
|
||||
@@ -405,9 +405,9 @@ func TestCreateAuthorityStrictDefaultsStayWithinActorPermissions(t *testing.T) {
|
||||
if err := db.Create(&apiPO{Path: "/menu/getMenu", Method: "POST"}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{AuthorityID: actorID})
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{AuthorityID: actorID})
|
||||
repo := &authorityAccessRepo{data: data}
|
||||
first := &biz.Authority{AuthorityID: 1401, AuthorityName: "no-defaults", ParentID: &actorID}
|
||||
first := &system.Authority{AuthorityID: 1401, AuthorityName: "no-defaults", ParentID: &actorID}
|
||||
if err := repo.CreateAuthority(ctx, first); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -429,7 +429,7 @@ func TestCreateAuthorityStrictDefaultsStayWithinActorPermissions(t *testing.T) {
|
||||
if err := db.Create(&policy).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
second := &biz.Authority{AuthorityID: 1402, AuthorityName: "owned-defaults", ParentID: &actorID}
|
||||
second := &system.Authority{AuthorityID: 1402, AuthorityName: "owned-defaults", ParentID: &actorID}
|
||||
if err := repo.CreateAuthority(ctx, second); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -476,21 +476,21 @@ func TestCopyAuthorityStrictValidatesCopiedMenusAndButtons(t *testing.T) {
|
||||
}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{AuthorityID: actorID})
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{AuthorityID: actorID})
|
||||
repo := &authorityAccessRepo{data: data}
|
||||
if err := repo.CopyAuthority(ctx, sourceID, &biz.Authority{AuthorityID: 1510, AuthorityName: "menu-fail", ParentID: &actorID}); err == nil {
|
||||
if err := repo.CopyAuthority(ctx, sourceID, &system.Authority{AuthorityID: 1510, AuthorityName: "menu-fail", ParentID: &actorID}); err == nil {
|
||||
t.Fatal("CopyAuthority() copied a menu not assigned to the actor")
|
||||
}
|
||||
if err := db.Where("sys_authority_authority_id = ? AND sys_base_menu_id = ?", sourceID, 11).Delete(&authorityMenuPO{}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := repo.CopyAuthority(ctx, sourceID, &biz.Authority{AuthorityID: 1511, AuthorityName: "button-fail", ParentID: &actorID}); err == nil {
|
||||
if err := repo.CopyAuthority(ctx, sourceID, &system.Authority{AuthorityID: 1511, AuthorityName: "button-fail", ParentID: &actorID}); err == nil {
|
||||
t.Fatal("CopyAuthority() copied a button not assigned to the actor")
|
||||
}
|
||||
if err := db.Where("authority_id = ? AND sys_base_menu_btn_id = ?", sourceID, 32).Delete(&authorityButtonPO{}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := repo.CopyAuthority(ctx, sourceID, &biz.Authority{AuthorityID: 1512, AuthorityName: "valid", ParentID: &actorID}); err != nil {
|
||||
if err := repo.CopyAuthority(ctx, sourceID, &system.Authority{AuthorityID: 1512, AuthorityName: "valid", ParentID: &actorID}); err != nil {
|
||||
t.Fatalf("CopyAuthority() rejected owned permissions: %v", err)
|
||||
}
|
||||
for _, failedID := range []uint{1510, 1511} {
|
||||
@@ -509,7 +509,7 @@ func TestAuthorityCreateAndCopyNormalizeZeroDataScope(t *testing.T) {
|
||||
db := data.gormDB.WithContext(context.Background())
|
||||
repo := &authorityAccessRepo{data: data}
|
||||
|
||||
created := &biz.Authority{AuthorityID: 1600, AuthorityName: "created"}
|
||||
created := &system.Authority{AuthorityID: 1600, AuthorityName: "created"}
|
||||
if err := repo.CreateAuthority(context.Background(), created); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -520,7 +520,7 @@ func TestAuthorityCreateAndCopyNormalizeZeroDataScope(t *testing.T) {
|
||||
if err := db.Create(&authorityPO{AuthorityID: 1601, AuthorityName: "source", DataScope: 3}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
copied := &biz.Authority{AuthorityID: 1602, AuthorityName: "copied"}
|
||||
copied := &system.Authority{AuthorityID: 1602, AuthorityName: "copied"}
|
||||
if err := repo.CopyAuthority(context.Background(), 1601, copied); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -549,13 +549,13 @@ func TestAuthorityMutationsRejectInvalidDataScope(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, scope := range []int{-1, 6} {
|
||||
if err := repo.CreateAuthority(context.Background(), &biz.Authority{AuthorityID: uint(1800 + scope + 1), AuthorityName: "invalid-create", DataScope: scope}); !errors.Is(err, errInvalidDataScope) {
|
||||
if err := repo.CreateAuthority(context.Background(), &system.Authority{AuthorityID: uint(1800 + scope + 1), AuthorityName: "invalid-create", DataScope: scope}); !errors.Is(err, errInvalidDataScope) {
|
||||
t.Fatalf("CreateAuthority() scope %d error = %v", scope, err)
|
||||
}
|
||||
if err := repo.CopyAuthority(context.Background(), 1700, &biz.Authority{AuthorityID: uint(1900 + scope + 1), AuthorityName: "invalid-copy", DataScope: scope}); !errors.Is(err, errInvalidDataScope) {
|
||||
if err := repo.CopyAuthority(context.Background(), 1700, &system.Authority{AuthorityID: uint(1900 + scope + 1), AuthorityName: "invalid-copy", DataScope: scope}); !errors.Is(err, errInvalidDataScope) {
|
||||
t.Fatalf("CopyAuthority() scope %d error = %v", scope, err)
|
||||
}
|
||||
if err := repo.UpdateAuthority(context.Background(), &biz.Authority{AuthorityID: 1701, AuthorityName: "invalid-update", DataScope: scope}); !errors.Is(err, errInvalidDataScope) {
|
||||
if err := repo.UpdateAuthority(context.Background(), &system.Authority{AuthorityID: 1701, AuthorityName: "invalid-update", DataScope: scope}); !errors.Is(err, errInvalidDataScope) {
|
||||
t.Fatalf("UpdateAuthority() scope %d error = %v", scope, err)
|
||||
}
|
||||
}
|
||||
@@ -568,7 +568,7 @@ func TestUpdateAuthorityZeroDataScopeKeepsStoredValue(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := (&authorityAccessRepo{data: data}).UpdateAuthority(context.Background(), &biz.Authority{AuthorityID: 2000, AuthorityName: "after", DataScope: 0}); err != nil {
|
||||
if err := (&authorityAccessRepo{data: data}).UpdateAuthority(context.Background(), &system.Authority{AuthorityID: 2000, AuthorityName: "after", DataScope: 0}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var stored authorityPO
|
||||
|
||||
@@ -2,9 +2,9 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz/system"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/pkg/database/pagination"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -23,13 +23,13 @@ type dataAccessLogPO struct {
|
||||
|
||||
func (dataAccessLogPO) TableName() string { return "sys_data_access_logs" }
|
||||
|
||||
func (r *auditRecorderRepo) RecordDataAccess(ctx context.Context, v *biz.DataAccessLog) error {
|
||||
func (r *auditRecorderRepo) RecordDataAccess(ctx context.Context, v *system.DataAccessLog) error {
|
||||
return r.data.DB().WithContext(ctx).Create(&dataAccessLogPO{EventType: v.EventType, TargetTable: v.TargetTable, Operation: v.Operation, UserID: v.UserID, AuthorityID: v.AuthorityID, Scope: v.Scope, RequestID: v.RequestID, Method: v.Method, Path: v.Path, Detail: v.Detail}).Error
|
||||
}
|
||||
func dataAccessFromPO(v dataAccessLogPO) *biz.DataAccessLog {
|
||||
return &biz.DataAccessLog{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, EventType: v.EventType, TargetTable: v.TargetTable, Operation: v.Operation, UserID: v.UserID, AuthorityID: v.AuthorityID, Scope: v.Scope, RequestID: v.RequestID, Method: v.Method, Path: v.Path, Detail: v.Detail}
|
||||
func dataAccessFromPO(v dataAccessLogPO) *system.DataAccessLog {
|
||||
return &system.DataAccessLog{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, EventType: v.EventType, TargetTable: v.TargetTable, Operation: v.Operation, UserID: v.UserID, AuthorityID: v.AuthorityID, Scope: v.Scope, RequestID: v.RequestID, Method: v.Method, Path: v.Path, Detail: v.Detail}
|
||||
}
|
||||
func (r *auditQueryRepo) ListDataAccess(ctx context.Context, page, size int, q *biz.DataAccessLog) ([]*biz.DataAccessLog, int64, error) {
|
||||
func (r *auditQueryRepo) ListDataAccess(ctx context.Context, page, size int, q *system.DataAccessLog) ([]*system.DataAccessLog, int64, error) {
|
||||
db := r.data.DB().WithContext(ctx).Model(&dataAccessLogPO{})
|
||||
if q != nil {
|
||||
if q.EventType != "" {
|
||||
@@ -47,7 +47,7 @@ func (r *auditQueryRepo) ListDataAccess(ctx context.Context, page, size int, q *
|
||||
if err := pagination.ApplyRequired(db.Order("id desc"), page, size, 100).Find(&pos).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]*biz.DataAccessLog, 0, len(pos))
|
||||
out := make([]*system.DataAccessLog, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
out = append(out, dataAccessFromPO(po))
|
||||
}
|
||||
|
||||
@@ -3,18 +3,17 @@ package system
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"kra/internal/biz/system"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type departmentRepo struct{ data Provider }
|
||||
|
||||
func NewDepartmentRepo(data Provider) biz.DepartmentRepo { return &departmentRepo{data: data} }
|
||||
func NewDepartmentRepo(data Provider) system.DepartmentRepo { return &departmentRepo{data: data} }
|
||||
|
||||
type departmentPO struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
@@ -47,11 +46,11 @@ type authorityDepartmentPO struct {
|
||||
|
||||
func (authorityDepartmentPO) TableName() string { return "sys_authority_departments" }
|
||||
|
||||
func deptFromPO(po departmentPO) *biz.Department {
|
||||
return &biz.Department{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Name: po.Name, ParentID: po.ParentID, Ancestors: po.Ancestors, Sort: po.Sort, LeaderID: po.LeaderID, Status: po.Status}
|
||||
func deptFromPO(po departmentPO) *system.Department {
|
||||
return &system.Department{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Name: po.Name, ParentID: po.ParentID, Ancestors: po.Ancestors, Sort: po.Sort, LeaderID: po.LeaderID, Status: po.Status}
|
||||
}
|
||||
|
||||
func (r *departmentRepo) attachDepartmentLeaders(ctx context.Context, departments []*biz.Department) error {
|
||||
func (r *departmentRepo) attachDepartmentLeaders(ctx context.Context, departments []*system.Department) error {
|
||||
leaderIDs := make([]uint, 0, len(departments))
|
||||
for _, department := range departments {
|
||||
if department.LeaderID != 0 {
|
||||
@@ -65,7 +64,7 @@ func (r *departmentRepo) attachDepartmentLeaders(ctx context.Context, department
|
||||
if err := r.data.DB().WithContext(ctx).Where("id IN ?", leaderIDs).Find(&leaders).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
leaderByID := make(map[uint]*biz.User, len(leaders))
|
||||
leaderByID := make(map[uint]*system.User, len(leaders))
|
||||
for i := range leaders {
|
||||
leaderByID[leaders[i].ID] = baseBizUser(&leaders[i])
|
||||
}
|
||||
@@ -74,7 +73,7 @@ func (r *departmentRepo) attachDepartmentLeaders(ctx context.Context, department
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (r *departmentRepo) CreateDepartment(ctx context.Context, v *biz.Department) error {
|
||||
func (r *departmentRepo) CreateDepartment(ctx context.Context, v *system.Department) error {
|
||||
v.Ancestors = "0"
|
||||
if v.ParentID != 0 {
|
||||
var parent departmentPO
|
||||
@@ -88,7 +87,7 @@ func (r *departmentRepo) CreateDepartment(ctx context.Context, v *biz.Department
|
||||
}
|
||||
return r.data.DB().WithContext(ctx).Create(&departmentPO{Name: v.Name, ParentID: v.ParentID, Ancestors: v.Ancestors, Sort: v.Sort, LeaderID: v.LeaderID, Status: v.Status}).Error
|
||||
}
|
||||
func (r *departmentRepo) UpdateDepartment(ctx context.Context, v *biz.Department) error {
|
||||
func (r *departmentRepo) UpdateDepartment(ctx context.Context, v *system.Department) error {
|
||||
if v.ParentID == v.ID {
|
||||
return errors.New("父部门不能是自己")
|
||||
}
|
||||
@@ -130,18 +129,18 @@ func (r *departmentRepo) DeleteDepartment(ctx context.Context, id uint) error {
|
||||
}
|
||||
return r.data.DB().WithContext(ctx).Delete(&departmentPO{}, id).Error
|
||||
}
|
||||
func (r *departmentRepo) FindDepartment(ctx context.Context, id uint) (*biz.Department, error) {
|
||||
func (r *departmentRepo) FindDepartment(ctx context.Context, id uint) (*system.Department, error) {
|
||||
var po departmentPO
|
||||
if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
department := deptFromPO(po)
|
||||
if err := r.attachDepartmentLeaders(ctx, []*biz.Department{department}); err != nil {
|
||||
if err := r.attachDepartmentLeaders(ctx, []*system.Department{department}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return department, nil
|
||||
}
|
||||
func (r *departmentRepo) ListDepartments(ctx context.Context, name string) ([]*biz.Department, error) {
|
||||
func (r *departmentRepo) ListDepartments(ctx context.Context, name string) ([]*system.Department, error) {
|
||||
var pos []departmentPO
|
||||
db := r.data.DB().WithContext(ctx).Order("sort")
|
||||
if name != "" {
|
||||
@@ -150,15 +149,15 @@ func (r *departmentRepo) ListDepartments(ctx context.Context, name string) ([]*b
|
||||
if err := db.Find(&pos).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodes := map[uint]*biz.Department{}
|
||||
items := make([]*biz.Department, 0, len(pos))
|
||||
nodes := map[uint]*system.Department{}
|
||||
items := make([]*system.Department, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
item := deptFromPO(po)
|
||||
if name == "" {
|
||||
// Recursive GORM child queries initialize leaf collections to an empty
|
||||
// slice. Preserve that tree response shape while search and single-item
|
||||
// queries keep the model's nil children value.
|
||||
item.Children = []*biz.Department{}
|
||||
item.Children = []*system.Department{}
|
||||
}
|
||||
nodes[po.ID] = item
|
||||
items = append(items, item)
|
||||
@@ -169,7 +168,7 @@ func (r *departmentRepo) ListDepartments(ctx context.Context, name string) ([]*b
|
||||
if name != "" {
|
||||
return items, nil
|
||||
}
|
||||
roots := make([]*biz.Department, 0)
|
||||
roots := make([]*system.Department, 0)
|
||||
for _, po := range pos {
|
||||
n := nodes[po.ID]
|
||||
if p := nodes[po.ParentID]; p != nil {
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"kra/internal/biz/system"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/pkg/database/pagination"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
type dictionaryRepo struct{ data Provider }
|
||||
|
||||
func NewDictionaryRepo(data Provider) biz.DictionaryRepo { return &dictionaryRepo{data: data} }
|
||||
func NewDictionaryRepo(data Provider) system.DictionaryRepo { return &dictionaryRepo{data: data} }
|
||||
|
||||
type dictionaryPO struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
@@ -49,17 +49,17 @@ type dictionaryDetailPO struct {
|
||||
|
||||
func (dictionaryDetailPO) TableName() string { return "sys_dictionary_details" }
|
||||
|
||||
func dictionaryFromPO(po dictionaryPO) *biz.Dictionary {
|
||||
return &biz.Dictionary{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Name: po.Name, Type: po.Type, Status: po.Status, Desc: po.Desc, ParentID: po.ParentID}
|
||||
func dictionaryFromPO(po dictionaryPO) *system.Dictionary {
|
||||
return &system.Dictionary{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Name: po.Name, Type: po.Type, Status: po.Status, Desc: po.Desc, ParentID: po.ParentID}
|
||||
}
|
||||
func detailFromPO(po dictionaryDetailPO) *biz.DictionaryDetail {
|
||||
return &biz.DictionaryDetail{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Label: po.Label, Value: po.Value, Extend: po.Extend, Status: po.Status, Sort: po.Sort, DictionaryID: po.DictionaryID, ParentID: po.ParentID, Level: po.Level, Path: po.Path}
|
||||
func detailFromPO(po dictionaryDetailPO) *system.DictionaryDetail {
|
||||
return &system.DictionaryDetail{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Label: po.Label, Value: po.Value, Extend: po.Extend, Status: po.Status, Sort: po.Sort, DictionaryID: po.DictionaryID, ParentID: po.ParentID, Level: po.Level, Path: po.Path}
|
||||
}
|
||||
func parameterFromPO(po parameterPO) *biz.SystemParameter {
|
||||
return &biz.SystemParameter{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Name: po.Name, Key: po.Key, Value: po.Value, Desc: po.Desc}
|
||||
func parameterFromPO(po parameterPO) *system.SystemParameter {
|
||||
return &system.SystemParameter{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Name: po.Name, Key: po.Key, Value: po.Value, Desc: po.Desc}
|
||||
}
|
||||
|
||||
func (r *dictionaryRepo) CreateDictionary(ctx context.Context, v *biz.Dictionary) error {
|
||||
func (r *dictionaryRepo) CreateDictionary(ctx context.Context, v *system.Dictionary) error {
|
||||
var existing dictionaryPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("type = ?", v.Type).First(&existing).Error; err == nil {
|
||||
return errors.New("存在相同的type,不允许创建")
|
||||
@@ -75,7 +75,7 @@ func (r *dictionaryRepo) CreateDictionary(ctx context.Context, v *biz.Dictionary
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *dictionaryRepo) ImportDictionary(ctx context.Context, dictionary *biz.Dictionary, details []*biz.DictionaryDetail) error {
|
||||
func (r *dictionaryRepo) ImportDictionary(ctx context.Context, dictionary *system.Dictionary, details []*system.DictionaryDetail) error {
|
||||
var existing dictionaryPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("type = ?", dictionary.Type).First(&existing).Error; !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errors.New("存在相同的type,不允许导入")
|
||||
@@ -119,7 +119,7 @@ func (r *dictionaryRepo) ImportDictionary(ctx context.Context, dictionary *biz.D
|
||||
return nil
|
||||
})
|
||||
}
|
||||
func (r *dictionaryRepo) UpdateDictionary(ctx context.Context, v *biz.Dictionary) error {
|
||||
func (r *dictionaryRepo) UpdateDictionary(ctx context.Context, v *system.Dictionary) error {
|
||||
db := r.data.DB().WithContext(ctx)
|
||||
var current dictionaryPO
|
||||
if err := db.Where("id = ?", v.ID).First(¤t).Error; err != nil {
|
||||
@@ -175,7 +175,7 @@ func (r *dictionaryRepo) DeleteDictionary(ctx context.Context, id uint) error {
|
||||
}
|
||||
return db.Where("sys_dictionary_id = ?", id).Delete(&dictionaryDetailPO{}).Error
|
||||
}
|
||||
func (r *dictionaryRepo) FindDictionary(ctx context.Context, id uint, typ string, status *bool, details bool) (*biz.Dictionary, error) {
|
||||
func (r *dictionaryRepo) FindDictionary(ctx context.Context, id uint, typ string, status *bool, details bool) (*system.Dictionary, error) {
|
||||
var po dictionaryPO
|
||||
db := r.data.DB().WithContext(ctx)
|
||||
active := true
|
||||
@@ -192,7 +192,7 @@ func (r *dictionaryRepo) FindDictionary(ctx context.Context, id uint, typ string
|
||||
if err = db.Where("sys_dictionary_id = ? AND status = ?", out.ID, true).Order("sort").Find(&detailPOs).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out.Details = make([]*biz.DictionaryDetail, 0, len(detailPOs))
|
||||
out.Details = make([]*system.DictionaryDetail, 0, len(detailPOs))
|
||||
for _, detail := range detailPOs {
|
||||
out.Details = append(out.Details, detailFromPO(detail))
|
||||
}
|
||||
@@ -200,7 +200,7 @@ func (r *dictionaryRepo) FindDictionary(ctx context.Context, id uint, typ string
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (r *dictionaryRepo) ExportDictionary(ctx context.Context, id uint) (*biz.Dictionary, error) {
|
||||
func (r *dictionaryRepo) ExportDictionary(ctx context.Context, id uint) (*system.Dictionary, error) {
|
||||
var po dictionaryPO
|
||||
if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -210,13 +210,13 @@ func (r *dictionaryRepo) ExportDictionary(ctx context.Context, id uint) (*biz.Di
|
||||
if err := r.data.DB().WithContext(ctx).Where("sys_dictionary_id = ?", id).Order("sort").Find(&detailPOs).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value.Details = make([]*biz.DictionaryDetail, 0, len(detailPOs))
|
||||
value.Details = make([]*system.DictionaryDetail, 0, len(detailPOs))
|
||||
for _, detail := range detailPOs {
|
||||
value.Details = append(value.Details, detailFromPO(detail))
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
func (r *dictionaryRepo) ListDictionaries(ctx context.Context, page, size int, name, typ string, details bool) ([]*biz.Dictionary, int64, error) {
|
||||
func (r *dictionaryRepo) ListDictionaries(ctx context.Context, page, size int, name, typ string, details bool) ([]*system.Dictionary, int64, error) {
|
||||
db := r.data.DB().WithContext(ctx).Model(&dictionaryPO{})
|
||||
if name != "" {
|
||||
like := "%" + name + "%"
|
||||
@@ -265,16 +265,16 @@ func (r *dictionaryRepo) ListDictionaries(ctx context.Context, page, size int, n
|
||||
detailsByDictionary[value.DictionaryID] = append(detailsByDictionary[value.DictionaryID], value)
|
||||
}
|
||||
}
|
||||
out := make([]*biz.Dictionary, 0, len(pos))
|
||||
out := make([]*system.Dictionary, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
v := dictionaryFromPO(po)
|
||||
if details {
|
||||
v.Details = make([]*biz.DictionaryDetail, 0, len(detailsByDictionary[v.ID]))
|
||||
v.Details = make([]*system.DictionaryDetail, 0, len(detailsByDictionary[v.ID]))
|
||||
for _, detail := range detailsByDictionary[v.ID] {
|
||||
v.Details = append(v.Details, detailFromPO(detail))
|
||||
}
|
||||
} else {
|
||||
v.Children = make([]*biz.Dictionary, 0, len(childrenByParent[po.ID]))
|
||||
v.Children = make([]*system.Dictionary, 0, len(childrenByParent[po.ID]))
|
||||
for _, child := range childrenByParent[po.ID] {
|
||||
v.Children = append(v.Children, dictionaryFromPO(child))
|
||||
}
|
||||
@@ -284,7 +284,7 @@ func (r *dictionaryRepo) ListDictionaries(ctx context.Context, page, size int, n
|
||||
return out, total, nil
|
||||
}
|
||||
|
||||
func (r *dictionaryRepo) CreateDictionaryDetail(ctx context.Context, v *biz.DictionaryDetail) error {
|
||||
func (r *dictionaryRepo) CreateDictionaryDetail(ctx context.Context, v *system.DictionaryDetail) error {
|
||||
po := dictionaryDetailPO{Label: v.Label, Value: v.Value, Extend: v.Extend, Status: v.Status, Sort: v.Sort, DictionaryID: v.DictionaryID, ParentID: v.ParentID}
|
||||
po.Level = 0
|
||||
po.Path = ""
|
||||
@@ -304,7 +304,7 @@ func (r *dictionaryRepo) CreateDictionaryDetail(ctx context.Context, v *biz.Dict
|
||||
v.Path = po.Path
|
||||
return nil
|
||||
}
|
||||
func (r *dictionaryRepo) UpdateDictionaryDetail(ctx context.Context, v *biz.DictionaryDetail) error {
|
||||
func (r *dictionaryRepo) UpdateDictionaryDetail(ctx context.Context, v *system.DictionaryDetail) error {
|
||||
var po dictionaryDetailPO
|
||||
db := r.data.DB().WithContext(ctx)
|
||||
if err := db.First(&po, v.ID).Error; err != nil {
|
||||
@@ -359,14 +359,14 @@ func (r *dictionaryRepo) DeleteDictionaryDetail(ctx context.Context, id uint) er
|
||||
}
|
||||
return r.data.DB().WithContext(ctx).Delete(&dictionaryDetailPO{}, id).Error
|
||||
}
|
||||
func (r *dictionaryRepo) FindDictionaryDetail(ctx context.Context, id uint) (*biz.DictionaryDetail, error) {
|
||||
func (r *dictionaryRepo) FindDictionaryDetail(ctx context.Context, id uint) (*system.DictionaryDetail, error) {
|
||||
var po dictionaryDetailPO
|
||||
if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return detailFromPO(po), nil
|
||||
}
|
||||
func (r *dictionaryRepo) ListDictionaryDetails(ctx context.Context, page, size int, filter biz.DictionaryDetailFilter) ([]*biz.DictionaryDetail, int64, error) {
|
||||
func (r *dictionaryRepo) ListDictionaryDetails(ctx context.Context, page, size int, filter system.DictionaryDetailFilter) ([]*system.DictionaryDetail, int64, error) {
|
||||
db := r.data.DB().WithContext(ctx).Model(&dictionaryDetailPO{})
|
||||
if filter.DictionaryID != 0 {
|
||||
db = db.Where("sys_dictionary_id = ?", filter.DictionaryID)
|
||||
@@ -394,13 +394,13 @@ func (r *dictionaryRepo) ListDictionaryDetails(ctx context.Context, page, size i
|
||||
if err := pagination.ApplyRequired(db.Order("sort,id"), page, size, 100).Find(&pos).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]*biz.DictionaryDetail, 0, len(pos))
|
||||
out := make([]*system.DictionaryDetail, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
out = append(out, detailFromPO(po))
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func (r *dictionaryRepo) DictionaryDetailTree(ctx context.Context, dictionaryID uint, typ string) ([]*biz.DictionaryDetail, error) {
|
||||
func (r *dictionaryRepo) DictionaryDetailTree(ctx context.Context, dictionaryID uint, typ string) ([]*system.DictionaryDetail, error) {
|
||||
if dictionaryID == 0 {
|
||||
// The tree-by-type endpoint resolves only by dictionary type. Unlike the
|
||||
// public dictionary lookup, it does not require the dictionary itself to
|
||||
@@ -415,7 +415,7 @@ func (r *dictionaryRepo) DictionaryDetailTree(ctx context.Context, dictionaryID
|
||||
if err := r.data.DB().WithContext(ctx).Where("sys_dictionary_id = ? AND parent_id IS NULL", dictionaryID).Order("sort").Find(&pos).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
roots := make([]*biz.DictionaryDetail, 0, len(pos))
|
||||
roots := make([]*system.DictionaryDetail, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
item := detailFromPO(po)
|
||||
if err := r.loadDictionaryDetailChildren(ctx, item); err != nil {
|
||||
@@ -426,7 +426,7 @@ func (r *dictionaryRepo) DictionaryDetailTree(ctx context.Context, dictionaryID
|
||||
return roots, nil
|
||||
}
|
||||
|
||||
func (r *dictionaryRepo) DictionaryDetailsByParent(ctx context.Context, dictionaryID uint, parentID *uint, includeChildren bool) ([]*biz.DictionaryDetail, error) {
|
||||
func (r *dictionaryRepo) DictionaryDetailsByParent(ctx context.Context, dictionaryID uint, parentID *uint, includeChildren bool) ([]*system.DictionaryDetail, error) {
|
||||
db := r.data.DB().WithContext(ctx).Where("sys_dictionary_id = ?", dictionaryID)
|
||||
if parentID == nil {
|
||||
db = db.Where("parent_id IS NULL")
|
||||
@@ -437,7 +437,7 @@ func (r *dictionaryRepo) DictionaryDetailsByParent(ctx context.Context, dictiona
|
||||
if err := db.Order("sort").Find(&pos).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := make([]*biz.DictionaryDetail, 0, len(pos))
|
||||
items := make([]*system.DictionaryDetail, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
item := detailFromPO(po)
|
||||
if includeChildren {
|
||||
@@ -450,12 +450,12 @@ func (r *dictionaryRepo) DictionaryDetailsByParent(ctx context.Context, dictiona
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (r *dictionaryRepo) loadDictionaryDetailChildren(ctx context.Context, parent *biz.DictionaryDetail) error {
|
||||
func (r *dictionaryRepo) loadDictionaryDetailChildren(ctx context.Context, parent *system.DictionaryDetail) error {
|
||||
var pos []dictionaryDetailPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("parent_id = ?", parent.ID).Order("sort").Find(&pos).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
parent.Children = make([]*biz.DictionaryDetail, 0, len(pos))
|
||||
parent.Children = make([]*system.DictionaryDetail, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
child := detailFromPO(po)
|
||||
if err := r.loadDictionaryDetailChildren(ctx, child); err != nil {
|
||||
|
||||
@@ -2,9 +2,8 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz/system"
|
||||
"testing"
|
||||
|
||||
"kra/internal/biz"
|
||||
)
|
||||
|
||||
func TestDictionaryListPreservesPreloadCollectionShapes(t *testing.T) {
|
||||
@@ -12,10 +11,10 @@ func TestDictionaryListPreservesPreloadCollectionShapes(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
repo := &dictionaryRepo{data: data}
|
||||
|
||||
if err := repo.CreateDictionary(ctx, &biz.Dictionary{Name: "status", Type: "status"}); err != nil {
|
||||
if err := repo.CreateDictionary(ctx, &system.Dictionary{Name: "status", Type: "status"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := repo.CreateDictionary(ctx, &biz.Dictionary{Name: "kind", Type: "kind"}); err != nil {
|
||||
if err := repo.CreateDictionary(ctx, &system.Dictionary{Name: "kind", Type: "kind"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -47,10 +46,10 @@ func TestImportDictionaryRejectsDuplicateType(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
repo := &dictionaryRepo{data: data}
|
||||
|
||||
if err := repo.CreateDictionary(ctx, &biz.Dictionary{Name: "first", Type: "duplicate"}); err != nil {
|
||||
if err := repo.CreateDictionary(ctx, &system.Dictionary{Name: "first", Type: "duplicate"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := repo.ImportDictionary(ctx, &biz.Dictionary{Name: "second", Type: "duplicate"}, nil); err == nil || err.Error() != "存在相同的type,不允许导入" {
|
||||
if err := repo.ImportDictionary(ctx, &system.Dictionary{Name: "second", Type: "duplicate"}, nil); err == nil || err.Error() != "存在相同的type,不允许导入" {
|
||||
t.Fatalf("duplicate import error = %v", err)
|
||||
}
|
||||
|
||||
@@ -68,16 +67,16 @@ func TestDictionaryTreeAndParentQueriesPreserveLoadedChildrenShape(t *testing.T)
|
||||
ctx := context.Background()
|
||||
repo := &dictionaryRepo{data: data}
|
||||
|
||||
dictionary := &biz.Dictionary{Name: "tree", Type: "tree"}
|
||||
dictionary := &system.Dictionary{Name: "tree", Type: "tree"}
|
||||
if err := repo.CreateDictionary(ctx, dictionary); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
parent := &biz.DictionaryDetail{Label: "parent", Value: "parent", DictionaryID: dictionary.ID, Sort: 2}
|
||||
parent := &system.DictionaryDetail{Label: "parent", Value: "parent", DictionaryID: dictionary.ID, Sort: 2}
|
||||
if err := repo.CreateDictionaryDetail(ctx, parent); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
parentID := parent.ID
|
||||
child := &biz.DictionaryDetail{Label: "child", Value: "child", DictionaryID: dictionary.ID, ParentID: &parentID, Sort: 1}
|
||||
child := &system.DictionaryDetail{Label: "child", Value: "child", DictionaryID: dictionary.ID, ParentID: &parentID, Sort: 1}
|
||||
if err := repo.CreateDictionaryDetail(ctx, child); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -112,15 +111,15 @@ func TestDictionaryDetailListAppliesZeroLimit(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
repo := &dictionaryRepo{data: data}
|
||||
|
||||
dictionary := &biz.Dictionary{Name: "limited", Type: "limited"}
|
||||
dictionary := &system.Dictionary{Name: "limited", Type: "limited"}
|
||||
if err := repo.CreateDictionary(ctx, dictionary); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := repo.CreateDictionaryDetail(ctx, &biz.DictionaryDetail{Label: "one", Value: "one", DictionaryID: dictionary.ID}); err != nil {
|
||||
if err := repo.CreateDictionaryDetail(ctx, &system.DictionaryDetail{Label: "one", Value: "one", DictionaryID: dictionary.ID}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
items, total, err := repo.ListDictionaryDetails(ctx, 0, 0, biz.DictionaryDetailFilter{DictionaryID: dictionary.ID})
|
||||
items, total, err := repo.ListDictionaryDetails(ctx, 0, 0, system.DictionaryDetailFilter{DictionaryID: dictionary.ID})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -136,7 +135,7 @@ func TestUserListAppliesZeroLimitForNegativePageSize(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
items, total, err := (&userRepo{data: data}).ListUsers(ctx, 1, -1, &biz.UserListFilter{})
|
||||
items, total, err := (&userRepo{data: data}).ListUsers(ctx, 1, -1, &system.UserListFilter{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -150,7 +149,7 @@ func TestDepartmentTreeAndFlatResultsPreserveChildrenShape(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
repo := &departmentRepo{data: data}
|
||||
|
||||
root := &biz.Department{Name: "root"}
|
||||
root := &system.Department{Name: "root"}
|
||||
if err := repo.CreateDepartment(ctx, root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -158,7 +157,7 @@ func TestDepartmentTreeAndFlatResultsPreserveChildrenShape(t *testing.T) {
|
||||
if err := data.gormDB.WithContext(ctx).Where("name = ?", "root").First(&rootPO).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := repo.CreateDepartment(ctx, &biz.Department{Name: "child", ParentID: rootPO.ID}); err != nil {
|
||||
if err := repo.CreateDepartment(ctx, &system.Department{Name: "child", ParentID: rootPO.ID}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz/system"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/pkg/database/pagination"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -26,10 +26,10 @@ type errorRecordPO struct {
|
||||
|
||||
func (errorRecordPO) TableName() string { return "sys_error" }
|
||||
|
||||
func errorFromPO(v errorRecordPO) *biz.ErrorRecord {
|
||||
return &biz.ErrorRecord{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, Form: v.Form, Info: v.Info, Level: v.Level, RequestID: v.RequestID, TraceID: v.TraceID, Solution: v.Solution, Status: v.Status}
|
||||
func errorFromPO(v errorRecordPO) *system.ErrorRecord {
|
||||
return &system.ErrorRecord{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, Form: v.Form, Info: v.Info, Level: v.Level, RequestID: v.RequestID, TraceID: v.TraceID, Solution: v.Solution, Status: v.Status}
|
||||
}
|
||||
func (r *auditRecorderRepo) CreateError(ctx context.Context, v *biz.ErrorRecord) error {
|
||||
func (r *auditRecorderRepo) CreateError(ctx context.Context, v *system.ErrorRecord) error {
|
||||
if !r.data.DatabaseReady() {
|
||||
// Silently ignore error records before the database is initialized.
|
||||
return nil
|
||||
@@ -39,7 +39,7 @@ func (r *auditRecorderRepo) CreateError(ctx context.Context, v *biz.ErrorRecord)
|
||||
}
|
||||
return r.data.DB().WithContext(ctx).Create(&errorRecordPO{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, Form: v.Form, Info: v.Info, Level: v.Level, RequestID: v.RequestID, TraceID: v.TraceID, Solution: v.Solution, Status: v.Status}).Error
|
||||
}
|
||||
func (r *auditQueryRepo) UpdateError(ctx context.Context, v *biz.ErrorRecord) error {
|
||||
func (r *auditQueryRepo) UpdateError(ctx context.Context, v *system.ErrorRecord) error {
|
||||
updates := make(map[string]any, 9)
|
||||
if v.ID != 0 {
|
||||
updates["id"] = v.ID
|
||||
@@ -73,14 +73,14 @@ func (r *auditQueryRepo) UpdateError(ctx context.Context, v *biz.ErrorRecord) er
|
||||
func (r *auditQueryRepo) DeleteErrors(ctx context.Context, ids []uint) error {
|
||||
return r.data.DB().WithContext(ctx).Delete(&errorRecordPO{}, ids).Error
|
||||
}
|
||||
func (r *auditQueryRepo) FindError(ctx context.Context, id uint) (*biz.ErrorRecord, error) {
|
||||
func (r *auditQueryRepo) FindError(ctx context.Context, id uint) (*system.ErrorRecord, error) {
|
||||
var po errorRecordPO
|
||||
if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return errorFromPO(po), nil
|
||||
}
|
||||
func (r *auditQueryRepo) ListErrors(ctx context.Context, page, size int, q *biz.ErrorRecord) ([]*biz.ErrorRecord, int64, error) {
|
||||
func (r *auditQueryRepo) ListErrors(ctx context.Context, page, size int, q *system.ErrorRecord) ([]*system.ErrorRecord, int64, error) {
|
||||
db := r.data.DB().WithContext(ctx).Model(&errorRecordPO{})
|
||||
if q != nil {
|
||||
if len(q.CreatedAtRange) == 2 {
|
||||
@@ -101,7 +101,7 @@ func (r *auditQueryRepo) ListErrors(ctx context.Context, page, size int, q *biz.
|
||||
if err := pagination.Apply(db.Order("created_at desc"), page, size, 100).Find(&pos).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]*biz.ErrorRecord, 0, len(pos))
|
||||
out := make([]*system.ErrorRecord, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
out = append(out, errorFromPO(po))
|
||||
}
|
||||
|
||||
@@ -2,10 +2,9 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz/system"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
)
|
||||
|
||||
func errorString(value string) *string { return &value }
|
||||
@@ -41,7 +40,7 @@ func TestCreateErrorBeforeDatabaseInitializationIsNoop(t *testing.T) {
|
||||
t.Cleanup(func() { _ = sqlDB.Close() })
|
||||
recorder := &auditRecorderRepo{data: &Data{gormDB: newReloadableDB(db, nil)}}
|
||||
form := "后端"
|
||||
if err = recorder.CreateError(context.Background(), &biz.ErrorRecord{Form: &form}); err != nil {
|
||||
if err = recorder.CreateError(context.Background(), &system.ErrorRecord{Form: &form}); err != nil {
|
||||
t.Fatalf("uninitialized error write = %v, want nil", err)
|
||||
}
|
||||
}
|
||||
@@ -50,7 +49,7 @@ func TestCreateErrorPreservesNullableFieldsAndDefaultStatus(t *testing.T) {
|
||||
recorder, query := newErrorRecordRepos(t)
|
||||
createdAt := time.Date(2025, time.March, 4, 5, 6, 7, 0, time.UTC)
|
||||
form := "前端"
|
||||
if err := recorder.CreateError(context.Background(), &biz.ErrorRecord{
|
||||
if err := recorder.CreateError(context.Background(), &system.ErrorRecord{
|
||||
ID: 41,
|
||||
CreatedAt: createdAt,
|
||||
Form: &form,
|
||||
@@ -80,7 +79,7 @@ func TestCreateErrorPreservesNullableFieldsAndDefaultStatus(t *testing.T) {
|
||||
func TestUpdateErrorDistinguishesOmittedAndExplicitEmptyFields(t *testing.T) {
|
||||
recorder, query := newErrorRecordRepos(t)
|
||||
form, info, solution := "后端", "错误内容", "解决方案"
|
||||
if err := recorder.CreateError(context.Background(), &biz.ErrorRecord{
|
||||
if err := recorder.CreateError(context.Background(), &system.ErrorRecord{
|
||||
Form: &form,
|
||||
Info: &info,
|
||||
Solution: &solution,
|
||||
@@ -98,7 +97,7 @@ func TestUpdateErrorDistinguishesOmittedAndExplicitEmptyFields(t *testing.T) {
|
||||
}
|
||||
id := items[0].ID
|
||||
newForm := "服务端"
|
||||
if err = query.UpdateError(context.Background(), &biz.ErrorRecord{ID: id, Form: &newForm}); err != nil {
|
||||
if err = query.UpdateError(context.Background(), &system.ErrorRecord{ID: id, Form: &newForm}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := query.FindError(context.Background(), id)
|
||||
@@ -110,7 +109,7 @@ func TestUpdateErrorDistinguishesOmittedAndExplicitEmptyFields(t *testing.T) {
|
||||
}
|
||||
|
||||
changedCreatedAt := time.Date(2024, time.January, 2, 3, 4, 5, 0, time.UTC)
|
||||
if err = query.UpdateError(context.Background(), &biz.ErrorRecord{
|
||||
if err = query.UpdateError(context.Background(), &system.ErrorRecord{
|
||||
ID: id,
|
||||
CreatedAt: changedCreatedAt,
|
||||
Form: errorString(""),
|
||||
|
||||
@@ -5,12 +5,13 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
"kra/internal/biz"
|
||||
"kra/internal/biz/system"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type exportTemplatePO struct {
|
||||
@@ -59,27 +60,27 @@ func (exportJoinPO) TableName() string { return "sys_export_template_join" }
|
||||
|
||||
type exportRepo struct{ data Provider }
|
||||
|
||||
func NewExportRepo(data Provider) biz.ExportRepo { return &exportRepo{data: data} }
|
||||
func exportFromPO(po exportTemplatePO, conditions []exportConditionPO, joins []exportJoinPO) *biz.ExportTemplate {
|
||||
v := &biz.ExportTemplate{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, DBName: po.DBName, Name: po.Name, TableName: po.DBTableName, TemplateID: po.TemplateID, TemplateInfo: po.TemplateInfo, SQL: po.SQL, ImportSQL: po.ImportSQL, Limit: po.Limit, Order: po.Order}
|
||||
func NewExportRepo(data Provider) system.ExportRepo { return &exportRepo{data: data} }
|
||||
func exportFromPO(po exportTemplatePO, conditions []exportConditionPO, joins []exportJoinPO) *system.ExportTemplate {
|
||||
v := &system.ExportTemplate{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, DBName: po.DBName, Name: po.Name, TableName: po.DBTableName, TemplateID: po.TemplateID, TemplateInfo: po.TemplateInfo, SQL: po.SQL, ImportSQL: po.ImportSQL, Limit: po.Limit, Order: po.Order}
|
||||
if conditions != nil {
|
||||
v.Conditions = make([]biz.ExportCondition, 0, len(conditions))
|
||||
v.Conditions = make([]system.ExportCondition, 0, len(conditions))
|
||||
}
|
||||
for _, x := range conditions {
|
||||
v.Conditions = append(v.Conditions, biz.ExportCondition{ID: x.ID, CreatedAt: x.CreatedAt, UpdatedAt: x.UpdatedAt, TemplateID: x.TemplateID, From: x.From, Column: x.Column, Operator: x.Operator})
|
||||
v.Conditions = append(v.Conditions, system.ExportCondition{ID: x.ID, CreatedAt: x.CreatedAt, UpdatedAt: x.UpdatedAt, TemplateID: x.TemplateID, From: x.From, Column: x.Column, Operator: x.Operator})
|
||||
}
|
||||
if joins != nil {
|
||||
v.Joins = make([]biz.ExportJoin, 0, len(joins))
|
||||
v.Joins = make([]system.ExportJoin, 0, len(joins))
|
||||
}
|
||||
for _, x := range joins {
|
||||
v.Joins = append(v.Joins, biz.ExportJoin{ID: x.ID, CreatedAt: x.CreatedAt, UpdatedAt: x.UpdatedAt, TemplateID: x.TemplateID, Join: x.Join, Table: x.Table, On: x.On})
|
||||
v.Joins = append(v.Joins, system.ExportJoin{ID: x.ID, CreatedAt: x.CreatedAt, UpdatedAt: x.UpdatedAt, TemplateID: x.TemplateID, Join: x.Join, Table: x.Table, On: x.On})
|
||||
}
|
||||
return v
|
||||
}
|
||||
func exportToPO(v *biz.ExportTemplate) exportTemplatePO {
|
||||
func exportToPO(v *system.ExportTemplate) exportTemplatePO {
|
||||
return exportTemplatePO{ID: v.ID, DBName: v.DBName, Name: v.Name, DBTableName: v.TableName, TemplateID: v.TemplateID, TemplateInfo: v.TemplateInfo, SQL: v.SQL, ImportSQL: v.ImportSQL, Limit: v.Limit, Order: v.Order}
|
||||
}
|
||||
func (r *exportRepo) saveRelations(tx *gorm.DB, v *biz.ExportTemplate, resetIDs, replace, forceTemplateID bool) error {
|
||||
func (r *exportRepo) saveRelations(tx *gorm.DB, v *system.ExportTemplate, resetIDs, replace, forceTemplateID bool) error {
|
||||
if replace {
|
||||
if err := tx.Where("template_id = ?", v.TemplateID).Delete(&exportConditionPO{}).Error; err != nil {
|
||||
return err
|
||||
@@ -137,7 +138,7 @@ func (r *exportRepo) saveRelations(tx *gorm.DB, v *biz.ExportTemplate, resetIDs,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (r *exportRepo) CreateExportTemplate(ctx context.Context, v *biz.ExportTemplate) error {
|
||||
func (r *exportRepo) CreateExportTemplate(ctx context.Context, v *system.ExportTemplate) error {
|
||||
return r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
po := exportToPO(v)
|
||||
if err := tx.Create(&po).Error; err != nil {
|
||||
@@ -147,7 +148,7 @@ func (r *exportRepo) CreateExportTemplate(ctx context.Context, v *biz.ExportTemp
|
||||
return r.saveRelations(tx, v, false, false, true)
|
||||
})
|
||||
}
|
||||
func (r *exportRepo) UpdateExportTemplate(ctx context.Context, v *biz.ExportTemplate) error {
|
||||
func (r *exportRepo) UpdateExportTemplate(ctx context.Context, v *system.ExportTemplate) error {
|
||||
return r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
po := exportToPO(v)
|
||||
if err := tx.Model(&exportTemplatePO{}).Where("id = ?", v.ID).Updates(&po).Error; err != nil {
|
||||
@@ -159,7 +160,7 @@ func (r *exportRepo) UpdateExportTemplate(ctx context.Context, v *biz.ExportTemp
|
||||
func (r *exportRepo) DeleteExportTemplates(ctx context.Context, ids []uint) error {
|
||||
return r.data.DB().WithContext(ctx).Delete(&[]exportTemplatePO{}, "id IN ?", ids).Error
|
||||
}
|
||||
func (r *exportRepo) FindExportTemplate(ctx context.Context, id uint, tid string) (*biz.ExportTemplate, error) {
|
||||
func (r *exportRepo) FindExportTemplate(ctx context.Context, id uint, tid string) (*system.ExportTemplate, error) {
|
||||
var po exportTemplatePO
|
||||
db := r.data.DB().WithContext(ctx)
|
||||
var err error
|
||||
@@ -181,7 +182,7 @@ func (r *exportRepo) FindExportTemplate(ctx context.Context, id uint, tid string
|
||||
}
|
||||
return exportFromPO(po, conditions, joins), nil
|
||||
}
|
||||
func (r *exportRepo) ListExportTemplates(ctx context.Context, page, size int, q *biz.ExportTemplate) ([]*biz.ExportTemplate, int64, error) {
|
||||
func (r *exportRepo) ListExportTemplates(ctx context.Context, page, size int, q *system.ExportTemplate) ([]*system.ExportTemplate, int64, error) {
|
||||
db := r.data.DB().WithContext(ctx).Model(&exportTemplatePO{})
|
||||
if q != nil {
|
||||
if q.StartCreatedAt != nil && q.EndCreatedAt != nil {
|
||||
@@ -210,14 +211,14 @@ func (r *exportRepo) ListExportTemplates(ctx context.Context, page, size int, q
|
||||
if err := db.Find(&pos).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]*biz.ExportTemplate, 0, len(pos))
|
||||
out := make([]*system.ExportTemplate, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
out = append(out, exportFromPO(po, nil, nil))
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
|
||||
func (r *exportRepo) QueryExport(ctx context.Context, t *biz.ExportTemplate, params map[string]string) ([]map[string]any, string, error) {
|
||||
func (r *exportRepo) QueryExport(ctx context.Context, t *system.ExportTemplate, params map[string]string) ([]map[string]any, string, error) {
|
||||
selected, err := r.data.Database(t.DBName)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
@@ -327,7 +328,7 @@ func (r *exportRepo) QueryExport(ctx context.Context, t *biz.ExportTemplate, par
|
||||
return rows, preview, err
|
||||
}
|
||||
|
||||
func (r *exportRepo) PreviewExport(ctx context.Context, t *biz.ExportTemplate, params map[string]string) (string, error) {
|
||||
func (r *exportRepo) PreviewExport(ctx context.Context, t *system.ExportTemplate, params map[string]string) (string, error) {
|
||||
columns := make([]string, 0)
|
||||
decoder := json.NewDecoder(strings.NewReader(t.TemplateInfo))
|
||||
if token, err := decoder.Token(); err != nil || token != json.Delim('{') {
|
||||
@@ -437,7 +438,7 @@ func parseTemplateColumns(raw string) map[string]string {
|
||||
}
|
||||
return out
|
||||
}
|
||||
func (r *exportRepo) ImportExportRows(ctx context.Context, t *biz.ExportTemplate, rows []map[string]any) error {
|
||||
func (r *exportRepo) ImportExportRows(ctx context.Context, t *system.ExportTemplate, rows []map[string]any) error {
|
||||
// ImportSQL is checked verbatim. In particular, a whitespace-only value
|
||||
// is still treated as custom SQL and is allowed to return the driver's
|
||||
// native error instead of silently falling back to GORM insertion.
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"kra/internal/biz/system"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/integration/runtimeconfig"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -31,23 +31,23 @@ type integrationRuntimeProvider interface {
|
||||
IntegrationRuntime() *runtimeconfig.Store
|
||||
}
|
||||
|
||||
func NewIntegrationConfigRepo(data Provider) biz.IntegrationConfigRepo {
|
||||
func NewIntegrationConfigRepo(data Provider) system.IntegrationConfigRepo {
|
||||
return &integrationConfigRepo{data: data}
|
||||
}
|
||||
|
||||
func (r *integrationConfigRepo) ListIntegrationConfigs(ctx context.Context, kind string) ([]*biz.IntegrationConfig, error) {
|
||||
func (r *integrationConfigRepo) ListIntegrationConfigs(ctx context.Context, kind string) ([]*system.IntegrationConfig, error) {
|
||||
var rows []integrationConfigPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("kind = ?", kind).Order("provider ASC").Find(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*biz.IntegrationConfig, 0, len(rows))
|
||||
result := make([]*system.IntegrationConfig, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
result = append(result, integrationConfigFromPO(row))
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *integrationConfigRepo) FindIntegrationConfig(ctx context.Context, kind, provider string) (*biz.IntegrationConfig, error) {
|
||||
func (r *integrationConfigRepo) FindIntegrationConfig(ctx context.Context, kind, provider string) (*system.IntegrationConfig, error) {
|
||||
var row integrationConfigPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("kind = ? AND provider = ?", kind, provider).First(&row).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
@@ -58,14 +58,14 @@ func (r *integrationConfigRepo) FindIntegrationConfig(ctx context.Context, kind,
|
||||
return integrationConfigFromPO(row), nil
|
||||
}
|
||||
|
||||
func (r *integrationConfigRepo) SaveIntegrationConfig(ctx context.Context, config *biz.IntegrationConfig) error {
|
||||
func (r *integrationConfigRepo) SaveIntegrationConfig(ctx context.Context, config *system.IntegrationConfig) error {
|
||||
db := r.data.DB().WithContext(ctx)
|
||||
var row integrationConfigPO
|
||||
err := db.Where("kind = ? AND provider = ?", config.Kind, config.Provider).First(&row).Error
|
||||
values := integrationObject(config.Values)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
if config.Enabled {
|
||||
if err = biz.ValidateIntegrationConfig(config.Kind, config.Provider, values); err != nil {
|
||||
if err = system.ValidateIntegrationConfig(config.Kind, config.Provider, values); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,7 @@ func (r *integrationConfigRepo) SaveIntegrationConfig(ctx context.Context, confi
|
||||
}
|
||||
mergeIntegrationSecrets(config.Kind, config.Provider, values, integrationObject(json.RawMessage(row.Config)))
|
||||
if config.Enabled {
|
||||
if err = biz.ValidateIntegrationConfig(config.Kind, config.Provider, values); err != nil {
|
||||
if err = system.ValidateIntegrationConfig(config.Kind, config.Provider, values); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -116,11 +116,11 @@ func integrationRuntime(provider Provider) *runtimeconfig.Store {
|
||||
return nil
|
||||
}
|
||||
|
||||
func integrationConfigFromPO(row integrationConfigPO) *biz.IntegrationConfig {
|
||||
func integrationConfigFromPO(row integrationConfigPO) *system.IntegrationConfig {
|
||||
values := integrationObject(json.RawMessage(row.Config))
|
||||
maskIntegrationSecrets(row.Kind, row.Provider, values)
|
||||
encoded, _ := json.Marshal(values)
|
||||
return &biz.IntegrationConfig{Kind: row.Kind, Provider: row.Provider, Enabled: row.Enabled, Values: encoded}
|
||||
return &system.IntegrationConfig{Kind: row.Kind, Provider: row.Provider, Enabled: row.Enabled, Values: encoded}
|
||||
}
|
||||
|
||||
func integrationObject(raw json.RawMessage) map[string]any {
|
||||
@@ -165,7 +165,7 @@ func mergeIntegrationSecrets(kind, provider string, values, old map[string]any)
|
||||
|
||||
func integrationSecretFields(kind, provider string) map[string]bool {
|
||||
result := map[string]bool{}
|
||||
if definition, ok := biz.IntegrationDefinition(kind, provider); ok {
|
||||
if definition, ok := system.IntegrationDefinition(kind, provider); ok {
|
||||
for _, field := range definition.Fields {
|
||||
if field.Secret {
|
||||
result[field.Key] = true
|
||||
|
||||
@@ -3,9 +3,9 @@ package system
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"kra/internal/biz/system"
|
||||
"testing"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/integration/runtimeconfig"
|
||||
)
|
||||
|
||||
@@ -27,19 +27,19 @@ func TestIntegrationConfigSavePublishesUnmaskedRuntimeValues(t *testing.T) {
|
||||
provider := &integrationRuntimeTestProvider{Data: &Data{gormDB: newReloadableDB(db, nil)}, store: runtimeconfig.NewStore()}
|
||||
repo := &integrationConfigRepo{data: provider}
|
||||
|
||||
values := biz.DefaultIntegrationConfig(biz.IntegrationKindMQ, "rabbitmq")
|
||||
values := system.DefaultIntegrationConfig(system.IntegrationKindMQ, "rabbitmq")
|
||||
values["password"] = "runtime-secret"
|
||||
raw, _ := json.Marshal(values)
|
||||
if err = repo.SaveIntegrationConfig(context.Background(), &biz.IntegrationConfig{Kind: biz.IntegrationKindMQ, Provider: "rabbitmq", Enabled: true, Values: raw}); err != nil {
|
||||
if err = repo.SaveIntegrationConfig(context.Background(), &system.IntegrationConfig{Kind: system.IntegrationKindMQ, Provider: "rabbitmq", Enabled: true, Values: raw}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
values["password"] = "******"
|
||||
raw, _ = json.Marshal(values)
|
||||
if err = repo.SaveIntegrationConfig(context.Background(), &biz.IntegrationConfig{Kind: biz.IntegrationKindMQ, Provider: "rabbitmq", Enabled: true, Values: raw}); err != nil {
|
||||
if err = repo.SaveIntegrationConfig(context.Background(), &system.IntegrationConfig{Kind: system.IntegrationKindMQ, Provider: "rabbitmq", Enabled: true, Values: raw}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
active, ok := provider.store.Get(biz.IntegrationKindMQ, "rabbitmq")
|
||||
active, ok := provider.store.Get(system.IntegrationKindMQ, "rabbitmq")
|
||||
if !ok || !active.Enabled {
|
||||
t.Fatalf("runtime config = %#v, ok=%v", active, ok)
|
||||
}
|
||||
|
||||
@@ -7,14 +7,13 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"kra/internal/biz/system"
|
||||
"os"
|
||||
pathpkg "path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -26,21 +25,21 @@ const (
|
||||
func (r *logFileRepo) configuredLogRoot() (root string, exists bool, err error) {
|
||||
admin := r.data.Runtime().Admin()
|
||||
if admin == nil || admin.Zap == nil || strings.TrimSpace(admin.Zap.Director) == "" {
|
||||
return "", false, biz.ErrLogRootUnavailable
|
||||
return "", false, system.ErrLogRootUnavailable
|
||||
}
|
||||
root, err = filepath.Abs(admin.Zap.Director)
|
||||
if err != nil {
|
||||
return "", false, fmt.Errorf("%w: %v", biz.ErrLogRootUnavailable, err)
|
||||
return "", false, fmt.Errorf("%w: %v", system.ErrLogRootUnavailable, err)
|
||||
}
|
||||
info, err := os.Stat(root)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return root, false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return "", false, fmt.Errorf("%w: %v", biz.ErrLogRootUnavailable, err)
|
||||
return "", false, fmt.Errorf("%w: %v", system.ErrLogRootUnavailable, err)
|
||||
}
|
||||
if !info.IsDir() {
|
||||
return "", false, biz.ErrLogRootUnavailable
|
||||
return "", false, system.ErrLogRootUnavailable
|
||||
}
|
||||
return root, true, nil
|
||||
}
|
||||
@@ -52,12 +51,12 @@ func (r *logFileRepo) openConfiguredLogRoot() (root *os.Root, exists bool, err e
|
||||
}
|
||||
root, err = os.OpenRoot(rootPath)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("%w: %v", biz.ErrLogRootUnavailable, err)
|
||||
return nil, false, fmt.Errorf("%w: %v", system.ErrLogRootUnavailable, err)
|
||||
}
|
||||
return root, true, nil
|
||||
}
|
||||
|
||||
func (r *logFileRepo) LogDates(ctx context.Context, month string) ([]biz.LogDate, error) {
|
||||
func (r *logFileRepo) LogDates(ctx context.Context, month string) ([]system.LogDate, error) {
|
||||
if err := validateLogMonth(month); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -66,15 +65,15 @@ func (r *logFileRepo) LogDates(ctx context.Context, month string) ([]biz.LogDate
|
||||
return nil, err
|
||||
}
|
||||
if !exists {
|
||||
return []biz.LogDate{}, nil
|
||||
return []system.LogDate{}, nil
|
||||
}
|
||||
defer logRoot.Close()
|
||||
|
||||
entries, err := fs.ReadDir(logRoot.FS(), ".")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", biz.ErrLogRootUnavailable, err)
|
||||
return nil, fmt.Errorf("%w: %v", system.ErrLogRootUnavailable, err)
|
||||
}
|
||||
result := make([]biz.LogDate, 0)
|
||||
result := make([]system.LogDate, 0)
|
||||
for _, entry := range entries {
|
||||
if err = ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
@@ -90,14 +89,14 @@ func (r *logFileRepo) LogDates(ctx context.Context, month string) ([]biz.LogDate
|
||||
return nil, countErr
|
||||
}
|
||||
if count > 0 {
|
||||
result = append(result, biz.LogDate{Date: entry.Name(), FileCount: count})
|
||||
result = append(result, system.LogDate{Date: entry.Name(), FileCount: count})
|
||||
}
|
||||
}
|
||||
sort.Slice(result, func(i, j int) bool { return result[i].Date < result[j].Date })
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *logFileRepo) LogFiles(ctx context.Context, date string) ([]biz.LogFile, error) {
|
||||
func (r *logFileRepo) LogFiles(ctx context.Context, date string) ([]system.LogFile, error) {
|
||||
if err := validateLogDate(date); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -106,27 +105,27 @@ func (r *logFileRepo) LogFiles(ctx context.Context, date string) ([]biz.LogFile,
|
||||
return nil, err
|
||||
}
|
||||
if !exists {
|
||||
return []biz.LogFile{}, nil
|
||||
return []system.LogFile{}, nil
|
||||
}
|
||||
defer logRoot.Close()
|
||||
|
||||
info, err := logRoot.Lstat(date)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return []biz.LogFile{}, nil
|
||||
return []system.LogFile{}, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", biz.ErrLogRootUnavailable, err)
|
||||
return nil, fmt.Errorf("%w: %v", system.ErrLogRootUnavailable, err)
|
||||
}
|
||||
if info.Mode()&os.ModeSymlink != 0 || !info.IsDir() {
|
||||
return []biz.LogFile{}, nil
|
||||
return []system.LogFile{}, nil
|
||||
}
|
||||
dateRoot, err := logRoot.OpenRoot(date)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", biz.ErrLogRootUnavailable, err)
|
||||
return nil, fmt.Errorf("%w: %v", system.ErrLogRootUnavailable, err)
|
||||
}
|
||||
defer dateRoot.Close()
|
||||
|
||||
result := make([]biz.LogFile, 0)
|
||||
result := make([]system.LogFile, 0)
|
||||
err = fs.WalkDir(dateRoot.FS(), ".", func(path string, entry fs.DirEntry, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return walkErr
|
||||
@@ -151,24 +150,24 @@ func (r *logFileRepo) LogFiles(ctx context.Context, date string) ([]biz.LogFile,
|
||||
return infoErr
|
||||
}
|
||||
if fileInfo.Mode().IsRegular() {
|
||||
result = append(result, biz.LogFile{Path: path, Name: entry.Name(), Size: fileInfo.Size(), ModifiedAt: fileInfo.ModTime()})
|
||||
result = append(result, system.LogFile{Path: path, Name: entry.Name(), Size: fileInfo.Size(), ModifiedAt: fileInfo.ModTime()})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", biz.ErrLogRootUnavailable, err)
|
||||
return nil, fmt.Errorf("%w: %v", system.ErrLogRootUnavailable, err)
|
||||
}
|
||||
sort.Slice(result, func(i, j int) bool { return result[i].Path < result[j].Path })
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *logFileRepo) LogContent(ctx context.Context, date, apiPath string, cursor *int64) (*biz.LogContent, error) {
|
||||
result := &biz.LogContent{Date: date, Path: apiPath}
|
||||
func (r *logFileRepo) LogContent(ctx context.Context, date, apiPath string, cursor *int64) (*system.LogContent, error) {
|
||||
result := &system.LogContent{Date: date, Path: apiPath}
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cursor != nil && *cursor < 0 {
|
||||
return nil, biz.ErrInvalidLogPath
|
||||
return nil, system.ErrInvalidLogPath
|
||||
}
|
||||
file, info, err := r.openValidatedLogFile(date, apiPath)
|
||||
if err != nil {
|
||||
@@ -182,19 +181,19 @@ func (r *logFileRepo) LogContent(ctx context.Context, date, apiPath string, curs
|
||||
}
|
||||
start, limitedByBytes, err := findLogChunkStart(file, end)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", biz.ErrLogFileUnreadable, err)
|
||||
return nil, fmt.Errorf("%w: %v", system.ErrLogFileUnreadable, err)
|
||||
}
|
||||
data, err := readLogRange(file, start, end)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", biz.ErrLogFileUnreadable, err)
|
||||
return nil, fmt.Errorf("%w: %v", system.ErrLogFileUnreadable, err)
|
||||
}
|
||||
if int64(len(data)) < end-start {
|
||||
currentInfo, statErr := file.Stat()
|
||||
if statErr != nil || currentInfo.Size() < start {
|
||||
if statErr != nil {
|
||||
return nil, fmt.Errorf("%w: %v", biz.ErrLogFileUnreadable, statErr)
|
||||
return nil, fmt.Errorf("%w: %v", system.ErrLogFileUnreadable, statErr)
|
||||
}
|
||||
return nil, biz.ErrLogFileUnreadable
|
||||
return nil, system.ErrLogFileUnreadable
|
||||
}
|
||||
info = currentInfo
|
||||
}
|
||||
@@ -221,23 +220,23 @@ func (r *logFileRepo) openValidatedLogFile(date, apiPath string) (file *os.File,
|
||||
return nil, nil, err
|
||||
}
|
||||
if !exists {
|
||||
return nil, nil, biz.ErrLogFileNotFound
|
||||
return nil, nil, system.ErrLogFileNotFound
|
||||
}
|
||||
defer logRoot.Close()
|
||||
|
||||
dateInfo, err := logRoot.Lstat(date)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, nil, biz.ErrLogFileNotFound
|
||||
return nil, nil, system.ErrLogFileNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("%w: %v", biz.ErrLogFileUnreadable, err)
|
||||
return nil, nil, fmt.Errorf("%w: %v", system.ErrLogFileUnreadable, err)
|
||||
}
|
||||
if dateInfo.Mode()&os.ModeSymlink != 0 || !dateInfo.IsDir() {
|
||||
return nil, nil, biz.ErrInvalidLogPath
|
||||
return nil, nil, system.ErrInvalidLogPath
|
||||
}
|
||||
dateRoot, err := logRoot.OpenRoot(date)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("%w: %v", biz.ErrLogFileUnreadable, err)
|
||||
return nil, nil, fmt.Errorf("%w: %v", system.ErrLogFileUnreadable, err)
|
||||
}
|
||||
defer dateRoot.Close()
|
||||
|
||||
@@ -247,35 +246,35 @@ func (r *logFileRepo) openValidatedLogFile(date, apiPath string) (file *os.File,
|
||||
relativePath = filepath.Join(relativePath, segment)
|
||||
validatedInfo, err = dateRoot.Lstat(relativePath)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, nil, biz.ErrLogFileNotFound
|
||||
return nil, nil, system.ErrLogFileNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("%w: %v", biz.ErrLogFileUnreadable, err)
|
||||
return nil, nil, fmt.Errorf("%w: %v", system.ErrLogFileUnreadable, err)
|
||||
}
|
||||
if validatedInfo.Mode()&os.ModeSymlink != 0 {
|
||||
return nil, nil, biz.ErrInvalidLogPath
|
||||
return nil, nil, system.ErrInvalidLogPath
|
||||
}
|
||||
if index < len(segments)-1 && !validatedInfo.IsDir() {
|
||||
return nil, nil, biz.ErrInvalidLogPath
|
||||
return nil, nil, system.ErrInvalidLogPath
|
||||
}
|
||||
}
|
||||
if validatedInfo == nil || !validatedInfo.Mode().IsRegular() {
|
||||
return nil, nil, biz.ErrInvalidLogPath
|
||||
return nil, nil, system.ErrInvalidLogPath
|
||||
}
|
||||
file, err = dateRoot.Open(relativePath)
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, nil, biz.ErrLogFileNotFound
|
||||
return nil, nil, system.ErrLogFileNotFound
|
||||
}
|
||||
return nil, nil, fmt.Errorf("%w: %v", biz.ErrLogFileUnreadable, err)
|
||||
return nil, nil, fmt.Errorf("%w: %v", system.ErrLogFileUnreadable, err)
|
||||
}
|
||||
openedInfo, statErr := file.Stat()
|
||||
if statErr != nil || !openedInfo.Mode().IsRegular() || !os.SameFile(validatedInfo, openedInfo) {
|
||||
file.Close()
|
||||
if statErr != nil {
|
||||
return nil, nil, fmt.Errorf("%w: %v", biz.ErrLogFileUnreadable, statErr)
|
||||
return nil, nil, fmt.Errorf("%w: %v", system.ErrLogFileUnreadable, statErr)
|
||||
}
|
||||
return nil, nil, biz.ErrInvalidLogPath
|
||||
return nil, nil, system.ErrInvalidLogPath
|
||||
}
|
||||
return file, openedInfo, nil
|
||||
}
|
||||
@@ -283,7 +282,7 @@ func (r *logFileRepo) openValidatedLogFile(date, apiPath string) (file *os.File,
|
||||
func validateLogMonth(month string) error {
|
||||
parsed, err := time.Parse("2006-01", month)
|
||||
if err != nil || parsed.Format("2006-01") != month {
|
||||
return biz.ErrInvalidLogMonth
|
||||
return system.ErrInvalidLogMonth
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -291,23 +290,23 @@ func validateLogMonth(month string) error {
|
||||
func validateLogDate(date string) error {
|
||||
parsed, err := time.Parse("2006-01-02", date)
|
||||
if err != nil || parsed.Format("2006-01-02") != date {
|
||||
return biz.ErrInvalidLogDate
|
||||
return system.ErrInvalidLogDate
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateLogAPIPath(apiPath string) ([]string, error) {
|
||||
if apiPath == "" || strings.Contains(apiPath, "\\") || strings.Contains(apiPath, ":") || pathpkg.IsAbs(apiPath) {
|
||||
return nil, biz.ErrInvalidLogPath
|
||||
return nil, system.ErrInvalidLogPath
|
||||
}
|
||||
segments := strings.Split(apiPath, "/")
|
||||
for _, segment := range segments {
|
||||
if segment == "" || segment == "." || segment == ".." {
|
||||
return nil, biz.ErrInvalidLogPath
|
||||
return nil, system.ErrInvalidLogPath
|
||||
}
|
||||
}
|
||||
if !strings.EqualFold(pathpkg.Ext(apiPath), ".log") {
|
||||
return nil, biz.ErrInvalidLogPath
|
||||
return nil, system.ErrInvalidLogPath
|
||||
}
|
||||
return segments, nil
|
||||
}
|
||||
@@ -351,7 +350,7 @@ func countLogFiles(ctx context.Context, logRoot *os.Root, date string) (count in
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("%w: %v", biz.ErrLogRootUnavailable, err)
|
||||
return 0, fmt.Errorf("%w: %v", system.ErrLogRootUnavailable, err)
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ package system
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"kra/internal/biz/system"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/conf"
|
||||
)
|
||||
|
||||
@@ -44,7 +44,7 @@ func TestLogViewerRejectsPathTraversal(t *testing.T) {
|
||||
runtime := conf.NewRuntime(nil, &conf.AdminBackend{Zap: &conf.AdminBackend_Zap{Director: t.TempDir()}})
|
||||
repo := &logFileRepo{data: &Data{runtime: runtime}}
|
||||
_, err := repo.LogContent(context.Background(), "2026-08-16", "../application.log", nil)
|
||||
if !errors.Is(err, biz.ErrInvalidLogPath) {
|
||||
if !errors.Is(err, system.ErrInvalidLogPath) {
|
||||
t.Fatalf("expected invalid path error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz/system"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/pkg/database/pagination"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -23,7 +23,7 @@ type loginLogPO struct {
|
||||
|
||||
func (loginLogPO) TableName() string { return "sys_login_logs" }
|
||||
|
||||
func (r *auditRecorderRepo) RecordLogin(ctx context.Context, v *biz.LoginLog) error {
|
||||
func (r *auditRecorderRepo) RecordLogin(ctx context.Context, v *system.LoginLog) error {
|
||||
if !r.data.DatabaseReady() {
|
||||
// The login endpoint remains reachable before database initialization;
|
||||
// skip the audit write until storage is ready.
|
||||
@@ -31,10 +31,10 @@ func (r *auditRecorderRepo) RecordLogin(ctx context.Context, v *biz.LoginLog) er
|
||||
}
|
||||
return r.data.DB().WithContext(ctx).Create(&loginLogPO{Username: v.Username, IP: v.IP, Status: v.Status, ErrorMessage: v.ErrorMessage, Agent: v.Agent, UserID: v.UserID}).Error
|
||||
}
|
||||
func loginFromPO(v loginLogPO) *biz.LoginLog {
|
||||
return &biz.LoginLog{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, Username: v.Username, IP: v.IP, Status: v.Status, ErrorMessage: v.ErrorMessage, Agent: v.Agent, UserID: v.UserID}
|
||||
func loginFromPO(v loginLogPO) *system.LoginLog {
|
||||
return &system.LoginLog{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, Username: v.Username, IP: v.IP, Status: v.Status, ErrorMessage: v.ErrorMessage, Agent: v.Agent, UserID: v.UserID}
|
||||
}
|
||||
func (r *auditQueryRepo) ListLogins(ctx context.Context, page, size int, q *biz.LoginLog) ([]*biz.LoginLog, int64, error) {
|
||||
func (r *auditQueryRepo) ListLogins(ctx context.Context, page, size int, q *system.LoginLog) ([]*system.LoginLog, int64, error) {
|
||||
db := r.data.DB().WithContext(ctx).Model(&loginLogPO{})
|
||||
if q != nil {
|
||||
if q.Username != "" {
|
||||
@@ -59,7 +59,7 @@ func (r *auditQueryRepo) ListLogins(ctx context.Context, page, size int, q *biz.
|
||||
}
|
||||
}
|
||||
users := auditUsers(ctx, r.data.DB().WithContext(ctx), ids)
|
||||
out := make([]*biz.LoginLog, 0, len(pos))
|
||||
out := make([]*system.LoginLog, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
value := loginFromPO(po)
|
||||
value.User = users[po.UserID]
|
||||
@@ -67,7 +67,7 @@ func (r *auditQueryRepo) ListLogins(ctx context.Context, page, size int, q *biz.
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func (r *auditQueryRepo) FindLogin(ctx context.Context, id uint) (*biz.LoginLog, error) {
|
||||
func (r *auditQueryRepo) FindLogin(ctx context.Context, id uint) (*system.LoginLog, error) {
|
||||
var po loginLogPO
|
||||
if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -3,10 +3,9 @@ package system
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"kra/internal/biz/system"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -41,14 +40,14 @@ func (categoryPO) TableName() string { return "media_attachment_category" }
|
||||
|
||||
type mediaRepo struct{ data Provider }
|
||||
|
||||
func NewMediaRepo(data Provider) biz.MediaRepo { return &mediaRepo{data: data} }
|
||||
func mediaFromPO(v mediaPO) *biz.MediaFile {
|
||||
return &biz.MediaFile{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, Name: v.Name, CategoryID: v.CategoryID, URL: v.URL, Tag: v.Tag, Key: v.Key, Size: v.Size, Mime: v.Mime, MD5: v.MD5, UserID: v.UserID}
|
||||
func NewMediaRepo(data Provider) system.MediaRepo { return &mediaRepo{data: data} }
|
||||
func mediaFromPO(v mediaPO) *system.MediaFile {
|
||||
return &system.MediaFile{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, Name: v.Name, CategoryID: v.CategoryID, URL: v.URL, Tag: v.Tag, Key: v.Key, Size: v.Size, Mime: v.Mime, MD5: v.MD5, UserID: v.UserID}
|
||||
}
|
||||
func mediaToPO(v *biz.MediaFile) mediaPO {
|
||||
func mediaToPO(v *system.MediaFile) mediaPO {
|
||||
return mediaPO{ID: v.ID, Name: v.Name, CategoryID: v.CategoryID, URL: v.URL, Tag: v.Tag, Key: v.Key, Size: v.Size, Mime: v.Mime, MD5: v.MD5, UserID: v.UserID}
|
||||
}
|
||||
func (r *mediaRepo) CreateMedia(ctx context.Context, v *biz.MediaFile) error {
|
||||
func (r *mediaRepo) CreateMedia(ctx context.Context, v *system.MediaFile) error {
|
||||
po := mediaToPO(v)
|
||||
if err := r.data.DB().WithContext(ctx).Create(&po).Error; err != nil {
|
||||
return err
|
||||
@@ -58,21 +57,21 @@ func (r *mediaRepo) CreateMedia(ctx context.Context, v *biz.MediaFile) error {
|
||||
v.UpdatedAt = po.UpdatedAt
|
||||
return nil
|
||||
}
|
||||
func (r *mediaRepo) FindMedia(ctx context.Context, id uint) (*biz.MediaFile, error) {
|
||||
func (r *mediaRepo) FindMedia(ctx context.Context, id uint) (*system.MediaFile, error) {
|
||||
var po mediaPO
|
||||
if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mediaFromPO(po), nil
|
||||
}
|
||||
func (r *mediaRepo) FindMediaByHash(ctx context.Context, userID uint, hash string) (*biz.MediaFile, error) {
|
||||
func (r *mediaRepo) FindMediaByHash(ctx context.Context, userID uint, hash string) (*system.MediaFile, error) {
|
||||
var po mediaPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("user_id = ? AND md5 = ?", userID, hash).First(&po).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mediaFromPO(po), nil
|
||||
}
|
||||
func (r *mediaRepo) ListMedia(ctx context.Context, filter biz.MediaFilter) ([]*biz.MediaFile, int64, error) {
|
||||
func (r *mediaRepo) ListMedia(ctx context.Context, filter system.MediaFilter) ([]*system.MediaFile, int64, error) {
|
||||
db := r.data.DB().WithContext(ctx).Model(&mediaPO{})
|
||||
if filter.Keyword != "" {
|
||||
db = db.Where("name LIKE ?", "%"+filter.Keyword+"%")
|
||||
@@ -119,7 +118,7 @@ func (r *mediaRepo) ListMedia(ctx context.Context, filter biz.MediaFilter) ([]*b
|
||||
if err := db.Limit(limit).Offset(offset).Order(orderKey).Find(&pos).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]*biz.MediaFile, 0, len(pos))
|
||||
out := make([]*system.MediaFile, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
out = append(out, mediaFromPO(po))
|
||||
}
|
||||
@@ -142,14 +141,14 @@ func (r *mediaRepo) MediaKeyReferences(ctx context.Context, key string) (int64,
|
||||
err := r.data.DB().WithContext(ctx).Model(&mediaPO{}).Where(map[string]any{"key": key}).Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
func (r *mediaRepo) CreateMediaBatch(ctx context.Context, items []*biz.MediaFile) error {
|
||||
func (r *mediaRepo) CreateMediaBatch(ctx context.Context, items []*system.MediaFile) error {
|
||||
pos := make([]mediaPO, 0, len(items))
|
||||
for _, v := range items {
|
||||
pos = append(pos, mediaToPO(v))
|
||||
}
|
||||
return r.data.DB().WithContext(ctx).Create(&pos).Error
|
||||
}
|
||||
func (r *mediaRepo) SaveCategory(ctx context.Context, v *biz.AttachmentCategory) error {
|
||||
func (r *mediaRepo) SaveCategory(ctx context.Context, v *system.AttachmentCategory) error {
|
||||
db := r.data.DB().WithContext(ctx)
|
||||
var count int64
|
||||
query := db.Model(&categoryPO{}).Where("name = ? AND pid = ?", v.Name, v.ParentID)
|
||||
@@ -179,17 +178,17 @@ func (r *mediaRepo) DeleteCategory(ctx context.Context, id uint) error {
|
||||
}
|
||||
return r.data.DB().WithContext(ctx).Unscoped().Delete(&categoryPO{}, id).Error
|
||||
}
|
||||
func (r *mediaRepo) ListCategories(ctx context.Context) ([]*biz.AttachmentCategory, error) {
|
||||
func (r *mediaRepo) ListCategories(ctx context.Context) ([]*system.AttachmentCategory, error) {
|
||||
var pos []categoryPO
|
||||
if err := r.data.DB().WithContext(ctx).Find(&pos).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
byID := map[uint]*biz.AttachmentCategory{}
|
||||
byID := map[uint]*system.AttachmentCategory{}
|
||||
for _, po := range pos {
|
||||
// Children remains nil for leaf nodes, which serializes as null.
|
||||
byID[po.ID] = &biz.AttachmentCategory{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Name: po.Name, ParentID: po.ParentID}
|
||||
byID[po.ID] = &system.AttachmentCategory{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Name: po.Name, ParentID: po.ParentID}
|
||||
}
|
||||
roots := []*biz.AttachmentCategory{}
|
||||
roots := []*system.AttachmentCategory{}
|
||||
for _, po := range pos {
|
||||
v := byID[po.ID]
|
||||
if parent := byID[po.ParentID]; parent != nil {
|
||||
|
||||
@@ -2,20 +2,19 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz/system"
|
||||
"testing"
|
||||
|
||||
"kra/internal/biz"
|
||||
)
|
||||
|
||||
func TestMediaListKeepsZeroPageSizeContract(t *testing.T) {
|
||||
data := newTransactionTestData(t)
|
||||
repo := &mediaRepo{data: data}
|
||||
ctx := context.Background()
|
||||
if err := repo.CreateMedia(ctx, &biz.MediaFile{Name: "one.txt"}); err != nil {
|
||||
if err := repo.CreateMedia(ctx, &system.MediaFile{Name: "one.txt"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
items, total, err := repo.ListMedia(ctx, biz.MediaFilter{})
|
||||
items, total, err := repo.ListMedia(ctx, system.MediaFilter{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -3,10 +3,9 @@ package system
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"kra/internal/biz/system"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
@@ -42,30 +41,30 @@ type uploadChunkPO struct {
|
||||
|
||||
func (uploadChunkPO) TableName() string { return "media_upload_chunks" }
|
||||
|
||||
func uploadFromPO(v uploadSessionPO) *biz.UploadSession {
|
||||
return &biz.UploadSession{ID: v.ID, UserID: v.UserID, FileName: v.FileName, FileHash: v.FileHash, FileSize: v.FileSize, ChunkSize: v.ChunkSize, ChunkTotal: v.ChunkTotal, Status: v.Status, StorageKey: v.StorageKey, MediaID: v.MediaID}
|
||||
func uploadFromPO(v uploadSessionPO) *system.UploadSession {
|
||||
return &system.UploadSession{ID: v.ID, UserID: v.UserID, FileName: v.FileName, FileHash: v.FileHash, FileSize: v.FileSize, ChunkSize: v.ChunkSize, ChunkTotal: v.ChunkTotal, Status: v.Status, StorageKey: v.StorageKey, MediaID: v.MediaID}
|
||||
}
|
||||
func (r *mediaRepo) FindCompletedSession(ctx context.Context, userID uint, hash string) (*biz.UploadSession, error) {
|
||||
func (r *mediaRepo) FindCompletedSession(ctx context.Context, userID uint, hash string) (*system.UploadSession, error) {
|
||||
var po uploadSessionPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("user_id = ? AND file_hash = ? AND status = ?", userID, hash, "completed").First(&po).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, biz.ErrUploadSessionNotFound
|
||||
return nil, system.ErrUploadSessionNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return uploadFromPO(po), nil
|
||||
}
|
||||
func (r *mediaRepo) FindUploadingSession(ctx context.Context, userID uint, hash string) (*biz.UploadSession, error) {
|
||||
func (r *mediaRepo) FindUploadingSession(ctx context.Context, userID uint, hash string) (*system.UploadSession, error) {
|
||||
var po uploadSessionPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("user_id = ? AND file_hash = ? AND status = ?", userID, hash, "uploading").First(&po).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, biz.ErrUploadSessionNotFound
|
||||
return nil, system.ErrUploadSessionNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return uploadFromPO(po), nil
|
||||
}
|
||||
func (r *mediaRepo) CreateUploadSession(ctx context.Context, v *biz.UploadSession) error {
|
||||
func (r *mediaRepo) CreateUploadSession(ctx context.Context, v *system.UploadSession) error {
|
||||
po := uploadSessionPO{UserID: v.UserID, FileName: v.FileName, FileHash: v.FileHash, FileSize: v.FileSize, ChunkSize: v.ChunkSize, ChunkTotal: v.ChunkTotal, Status: v.Status}
|
||||
if err := r.data.DB().WithContext(ctx).Create(&po).Error; err != nil {
|
||||
return err
|
||||
@@ -73,7 +72,7 @@ func (r *mediaRepo) CreateUploadSession(ctx context.Context, v *biz.UploadSessio
|
||||
v.ID = po.ID
|
||||
return nil
|
||||
}
|
||||
func (r *mediaRepo) FindUploadSession(ctx context.Context, id uint) (*biz.UploadSession, error) {
|
||||
func (r *mediaRepo) FindUploadSession(ctx context.Context, id uint) (*system.UploadSession, error) {
|
||||
var po uploadSessionPO
|
||||
if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -95,21 +94,21 @@ func (r *mediaRepo) DeleteUploadSession(ctx context.Context, id uint) error {
|
||||
// for audit/recovery rather than physically removing it.
|
||||
return r.data.DB().WithContext(ctx).Delete(&uploadSessionPO{}, id).Error
|
||||
}
|
||||
func (r *mediaRepo) UpsertChunk(ctx context.Context, uploadID uint, v *biz.UploadChunk) error {
|
||||
func (r *mediaRepo) UpsertChunk(ctx context.Context, uploadID uint, v *system.UploadChunk) error {
|
||||
po := uploadChunkPO{UploadID: uploadID, ChunkIndex: v.Index, ChunkHash: v.Hash, Size: v.Size}
|
||||
return r.data.DB().WithContext(ctx).Clauses(clause.OnConflict{
|
||||
Columns: []clause.Column{{Name: "upload_id"}, {Name: "chunk_index"}},
|
||||
DoUpdates: clause.AssignmentColumns([]string{"chunk_hash", "size", "updated_at", "deleted_at"}),
|
||||
}).Create(&po).Error
|
||||
}
|
||||
func (r *mediaRepo) ListChunks(ctx context.Context, uploadID uint) ([]*biz.UploadChunk, error) {
|
||||
func (r *mediaRepo) ListChunks(ctx context.Context, uploadID uint) ([]*system.UploadChunk, error) {
|
||||
var pos []uploadChunkPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("upload_id = ?", uploadID).Order("chunk_index").Find(&pos).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]*biz.UploadChunk, 0, len(pos))
|
||||
out := make([]*system.UploadChunk, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
out = append(out, &biz.UploadChunk{Index: po.ChunkIndex, Hash: po.ChunkHash, Size: po.Size})
|
||||
out = append(out, &system.UploadChunk{Index: po.ChunkIndex, Hash: po.ChunkHash, Size: po.Size})
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
@@ -3,31 +3,30 @@ package system
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"kra/internal/biz/system"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type menuRepo struct{ data Provider }
|
||||
|
||||
func NewMenuRepo(data Provider) biz.MenuRepo { return &menuRepo{data: data} }
|
||||
func NewMenuRepo(data Provider) system.MenuRepo { return &menuRepo{data: data} }
|
||||
|
||||
func menuFromPO(po menuPO) *biz.Menu {
|
||||
func menuFromPO(po menuPO) *system.Menu {
|
||||
var deletedAt *time.Time
|
||||
if po.DeletedAt.Valid {
|
||||
value := po.DeletedAt.Time
|
||||
deletedAt = &value
|
||||
}
|
||||
return &biz.Menu{CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, DeletedAt: deletedAt, ID: po.ID, ParentID: po.ParentID, Path: po.Path, Name: po.Name, Hidden: po.Hidden, Component: po.Component, Sort: po.Sort, ActiveName: po.ActiveName, KeepAlive: po.KeepAlive, DefaultMenu: po.DefaultMenu, Title: po.Title, Icon: po.Icon, CloseTab: po.CloseTab, TransitionType: po.TransitionType}
|
||||
return &system.Menu{CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, DeletedAt: deletedAt, ID: po.ID, ParentID: po.ParentID, Path: po.Path, Name: po.Name, Hidden: po.Hidden, Component: po.Component, Sort: po.Sort, ActiveName: po.ActiveName, KeepAlive: po.KeepAlive, DefaultMenu: po.DefaultMenu, Title: po.Title, Icon: po.Icon, CloseTab: po.CloseTab, TransitionType: po.TransitionType}
|
||||
}
|
||||
|
||||
func menuToPO(v *biz.Menu) menuPO {
|
||||
func menuToPO(v *system.Menu) menuPO {
|
||||
return menuPO{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, ParentID: v.ParentID, Path: v.Path, Name: v.Name, Hidden: v.Hidden, Component: v.Component, Sort: v.Sort, ActiveName: v.ActiveName, KeepAlive: v.KeepAlive, DefaultMenu: v.DefaultMenu, Title: v.Title, Icon: v.Icon, CloseTab: v.CloseTab, TransitionType: v.TransitionType}
|
||||
}
|
||||
|
||||
func replaceMenuRelations(tx *gorm.DB, menu *biz.Menu) error {
|
||||
func replaceMenuRelations(tx *gorm.DB, menu *system.Menu) error {
|
||||
if err := tx.Unscoped().Where("sys_base_menu_id = ?", menu.ID).Delete(&menuParameterPO{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -37,7 +36,7 @@ func replaceMenuRelations(tx *gorm.DB, menu *biz.Menu) error {
|
||||
return createMenuRelations(tx, menu)
|
||||
}
|
||||
|
||||
func createMenuRelations(tx *gorm.DB, menu *biz.Menu) error {
|
||||
func createMenuRelations(tx *gorm.DB, menu *system.Menu) error {
|
||||
parameters := make([]menuParameterPO, 0, len(menu.Parameters))
|
||||
for _, parameter := range menu.Parameters {
|
||||
parameters = append(parameters, menuParameterPO{ID: parameter.ID, CreatedAt: parameter.CreatedAt, UpdatedAt: parameter.UpdatedAt, MenuID: menu.ID, Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
|
||||
@@ -57,7 +56,7 @@ func createMenuRelations(tx *gorm.DB, menu *biz.Menu) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *menuRepo) Create(ctx context.Context, v *biz.Menu) error {
|
||||
func (r *menuRepo) Create(ctx context.Context, v *system.Menu) error {
|
||||
return r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
var count int64
|
||||
if err := tx.Model(&menuPO{}).Where("name = ?", v.Name).Count(&count).Error; err != nil {
|
||||
@@ -98,7 +97,7 @@ func (r *menuRepo) Create(ctx context.Context, v *biz.Menu) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (r *menuRepo) Update(ctx context.Context, v *biz.Menu) error {
|
||||
func (r *menuRepo) Update(ctx context.Context, v *system.Menu) error {
|
||||
return r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
var old menuPO
|
||||
if err := tx.First(&old, v.ID).Error; err != nil {
|
||||
@@ -156,12 +155,12 @@ func (r *menuRepo) Delete(ctx context.Context, id uint) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (r *menuRepo) loadRelations(ctx context.Context, menu *biz.Menu) error {
|
||||
func (r *menuRepo) loadRelations(ctx context.Context, menu *system.Menu) error {
|
||||
var parameters []menuParameterPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("sys_base_menu_id = ?", menu.ID).Find(¶meters).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
menu.Parameters = make([]*biz.MenuParameter, 0, len(parameters))
|
||||
menu.Parameters = make([]*system.MenuParameter, 0, len(parameters))
|
||||
for _, parameter := range parameters {
|
||||
menu.Parameters = append(menu.Parameters, menuParameterFromPO(parameter))
|
||||
}
|
||||
@@ -169,14 +168,14 @@ func (r *menuRepo) loadRelations(ctx context.Context, menu *biz.Menu) error {
|
||||
if err := r.data.DB().WithContext(ctx).Where("sys_base_menu_id = ?", menu.ID).Find(&buttons).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
menu.Buttons = make([]*biz.MenuButton, 0, len(buttons))
|
||||
menu.Buttons = make([]*system.MenuButton, 0, len(buttons))
|
||||
for _, button := range buttons {
|
||||
menu.Buttons = append(menu.Buttons, menuButtonFromPO(button))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *menuRepo) Find(ctx context.Context, id uint) (*biz.Menu, error) {
|
||||
func (r *menuRepo) Find(ctx context.Context, id uint) (*system.Menu, error) {
|
||||
var po menuPO
|
||||
if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -188,11 +187,11 @@ func (r *menuRepo) Find(ctx context.Context, id uint) (*biz.Menu, error) {
|
||||
return menu, nil
|
||||
}
|
||||
|
||||
func (r *menuRepo) List(ctx context.Context) ([]*biz.Menu, error) {
|
||||
func (r *menuRepo) List(ctx context.Context) ([]*system.Menu, error) {
|
||||
var pos []menuPO
|
||||
db := r.data.DB().WithContext(ctx).Model(&menuPO{}).Order("sort")
|
||||
config := r.data.Runtime().Admin()
|
||||
if actor, ok := biz.ActorFromContext(ctx); ok && config != nil && config.System != nil && config.System.UseStrictAuth {
|
||||
if actor, ok := system.ActorFromContext(ctx); ok && config != nil && config.System != nil && config.System.UseStrictAuth {
|
||||
var authority authorityPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("authority_id = ?", actor.AuthorityID).First(&authority).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -204,20 +203,20 @@ func (r *menuRepo) List(ctx context.Context) ([]*biz.Menu, error) {
|
||||
if err := db.Find(&pos).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]*biz.Menu, 0, len(pos))
|
||||
out := make([]*system.Menu, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
out = append(out, menuFromPO(po))
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return out, nil
|
||||
}
|
||||
byID := make(map[uint]*biz.Menu, len(out))
|
||||
byID := make(map[uint]*system.Menu, len(out))
|
||||
ids := make([]uint, 0, len(out))
|
||||
for _, menu := range out {
|
||||
byID[menu.ID] = menu
|
||||
ids = append(ids, menu.ID)
|
||||
menu.Parameters = []*biz.MenuParameter{}
|
||||
menu.Buttons = []*biz.MenuButton{}
|
||||
menu.Parameters = []*system.MenuParameter{}
|
||||
menu.Buttons = []*system.MenuButton{}
|
||||
}
|
||||
var parameters []menuParameterPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("sys_base_menu_id IN ?", ids).Find(¶meters).Error; err != nil {
|
||||
@@ -238,22 +237,22 @@ func (r *menuRepo) List(ctx context.Context) ([]*biz.Menu, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func menuParameterFromPO(po menuParameterPO) *biz.MenuParameter {
|
||||
func menuParameterFromPO(po menuParameterPO) *system.MenuParameter {
|
||||
var deletedAt *time.Time
|
||||
if po.DeletedAt.Valid {
|
||||
value := po.DeletedAt.Time
|
||||
deletedAt = &value
|
||||
}
|
||||
return &biz.MenuParameter{CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, DeletedAt: deletedAt, ID: po.ID, MenuID: po.MenuID, Type: po.Type, Key: po.Key, Value: po.Value}
|
||||
return &system.MenuParameter{CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, DeletedAt: deletedAt, ID: po.ID, MenuID: po.MenuID, Type: po.Type, Key: po.Key, Value: po.Value}
|
||||
}
|
||||
|
||||
func menuButtonFromPO(po menuButtonPO) *biz.MenuButton {
|
||||
func menuButtonFromPO(po menuButtonPO) *system.MenuButton {
|
||||
var deletedAt *time.Time
|
||||
if po.DeletedAt.Valid {
|
||||
value := po.DeletedAt.Time
|
||||
deletedAt = &value
|
||||
}
|
||||
return &biz.MenuButton{CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, DeletedAt: deletedAt, ID: po.ID, Name: po.Name, Description: po.Description, MenuID: po.MenuID}
|
||||
return &system.MenuButton{CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, DeletedAt: deletedAt, ID: po.ID, Name: po.Name, Description: po.Description, MenuID: po.MenuID}
|
||||
}
|
||||
|
||||
func (r *menuRepo) SetAuthorityMenus(ctx context.Context, id uint, ids []uint) error {
|
||||
@@ -292,7 +291,7 @@ func (r *menuRepo) AuthorityMenuIDs(ctx context.Context, id uint) ([]uint, error
|
||||
// menus. In particular, it deliberately does not call List: List applies the
|
||||
// current request actor's strict-auth filter, which would incorrectly hide
|
||||
// menus when an administrator inspects a different (child) authority.
|
||||
func (r *menuRepo) ListAuthorityMenus(ctx context.Context, authorityID uint) ([]*biz.Menu, error) {
|
||||
func (r *menuRepo) ListAuthorityMenus(ctx context.Context, authorityID uint) ([]*system.Menu, error) {
|
||||
var pos []menuPO
|
||||
db := r.data.DB().WithContext(ctx).Model(&menuPO{}).
|
||||
Where("id IN (?)", r.data.DB().WithContext(ctx).Model(&authorityMenuPO{}).
|
||||
@@ -301,7 +300,7 @@ func (r *menuRepo) ListAuthorityMenus(ctx context.Context, authorityID uint) ([]
|
||||
if err := db.Find(&pos).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]*biz.Menu, 0, len(pos))
|
||||
out := make([]*system.Menu, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
// This endpoint does not preload Parameters or MenuBtn. Keep
|
||||
// both relations nil so the response shape remains compatible.
|
||||
|
||||
@@ -2,10 +2,10 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz/system"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/conf"
|
||||
)
|
||||
|
||||
@@ -31,7 +31,7 @@ func TestMenuCreatePreservesSubmittedTimestamps(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
createdAt := time.Date(2025, time.January, 2, 3, 4, 5, 0, time.UTC)
|
||||
updatedAt := time.Date(2025, time.February, 3, 4, 5, 6, 0, time.UTC)
|
||||
menu := &biz.Menu{CreatedAt: createdAt, UpdatedAt: updatedAt, Name: "created", Path: "created", Component: "view/created.vue", Title: "Created"}
|
||||
menu := &system.Menu{CreatedAt: createdAt, UpdatedAt: updatedAt, Name: "created", Path: "created", Component: "view/created.vue", Title: "Created"}
|
||||
|
||||
if err := (&menuRepo{data: data}).Create(ctx, menu); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -90,16 +90,16 @@ func TestMenuUpdateRebuildsRelationsWithoutChangingRequestedIdentity(t *testing.
|
||||
}
|
||||
|
||||
repo := &menuRepo{data: data}
|
||||
err := repo.Update(ctx, &biz.Menu{
|
||||
err := repo.Update(ctx, &system.Menu{
|
||||
ID: 10,
|
||||
Name: "after",
|
||||
Path: "after",
|
||||
Component: "view/after.vue",
|
||||
Title: "After",
|
||||
Parameters: []*biz.MenuParameter{{
|
||||
Parameters: []*system.MenuParameter{{
|
||||
ID: 21, CreatedAt: createdAt, UpdatedAt: updatedAt, Type: "params", Key: "new", Value: "value",
|
||||
}},
|
||||
Buttons: []*biz.MenuButton{{
|
||||
Buttons: []*system.MenuButton{{
|
||||
ID: 31, CreatedAt: createdAt, UpdatedAt: updatedAt, Name: "edit", Description: "new",
|
||||
}},
|
||||
})
|
||||
@@ -163,7 +163,7 @@ func TestSetMenuRolesStrictOnlyChangesManagedAuthorities(t *testing.T) {
|
||||
}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{AuthorityID: actorID})
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{AuthorityID: actorID})
|
||||
repo := &menuRepo{data: data}
|
||||
if err := repo.SetMenuRoles(ctx, menu.ID, []uint{siblingID}); err == nil {
|
||||
t.Fatal("SetMenuRoles() accepted an out-of-scope authority")
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"kra/internal/biz/system"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -98,4 +97,4 @@ func (menuParameterPO) TableName() string { return "sys_base_menu_parameters" }
|
||||
|
||||
type userRepo struct{ data Provider }
|
||||
|
||||
func NewUserRepo(data Provider) biz.UserRepo { return &userRepo{data: data} }
|
||||
func NewUserRepo(data Provider) system.UserRepo { return &userRepo{data: data} }
|
||||
|
||||
@@ -2,9 +2,9 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz/system"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/pkg/database/pagination"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -30,13 +30,13 @@ type operationPO struct {
|
||||
|
||||
func (operationPO) TableName() string { return "sys_operation_records" }
|
||||
|
||||
func (r *auditRecorderRepo) RecordOperation(ctx context.Context, v *biz.OperationRecord) error {
|
||||
func (r *auditRecorderRepo) RecordOperation(ctx context.Context, v *system.OperationRecord) error {
|
||||
return r.data.DB().WithContext(ctx).Create(&operationPO{IP: v.IP, Method: v.Method, Path: v.Path, Status: v.Status, LatencyMS: v.LatencyMS, Agent: v.Agent, ErrorMessage: v.ErrorMessage, Body: v.Body, Response: v.Response, UserID: v.UserID, RequestID: v.RequestID, TraceID: v.TraceID, DeviceID: v.DeviceID}).Error
|
||||
}
|
||||
func opFromPO(v operationPO) *biz.OperationRecord {
|
||||
return &biz.OperationRecord{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, IP: v.IP, Method: v.Method, Path: v.Path, Status: v.Status, LatencyMS: v.LatencyMS, Agent: v.Agent, ErrorMessage: v.ErrorMessage, Body: v.Body, Response: v.Response, UserID: v.UserID, RequestID: v.RequestID, TraceID: v.TraceID, DeviceID: v.DeviceID}
|
||||
func opFromPO(v operationPO) *system.OperationRecord {
|
||||
return &system.OperationRecord{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, IP: v.IP, Method: v.Method, Path: v.Path, Status: v.Status, LatencyMS: v.LatencyMS, Agent: v.Agent, ErrorMessage: v.ErrorMessage, Body: v.Body, Response: v.Response, UserID: v.UserID, RequestID: v.RequestID, TraceID: v.TraceID, DeviceID: v.DeviceID}
|
||||
}
|
||||
func (r *auditQueryRepo) ListOperations(ctx context.Context, page, size int, q *biz.OperationRecord) ([]*biz.OperationRecord, int64, error) {
|
||||
func (r *auditQueryRepo) ListOperations(ctx context.Context, page, size int, q *system.OperationRecord) ([]*system.OperationRecord, int64, error) {
|
||||
db := r.data.DB().WithContext(ctx).Model(&operationPO{})
|
||||
if q != nil {
|
||||
if q.Path != "" {
|
||||
@@ -58,7 +58,7 @@ func (r *auditQueryRepo) ListOperations(ctx context.Context, page, size int, q *
|
||||
return nil, 0, err
|
||||
}
|
||||
users := auditUsers(ctx, r.data.DB().WithContext(ctx), operationUserIDs(pos))
|
||||
out := make([]*biz.OperationRecord, 0, len(pos))
|
||||
out := make([]*system.OperationRecord, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
value := opFromPO(po)
|
||||
value.User = users[po.UserID]
|
||||
@@ -77,8 +77,8 @@ func operationUserIDs(values []operationPO) []uint {
|
||||
return ids
|
||||
}
|
||||
|
||||
func auditUsers(ctx context.Context, db *gorm.DB, ids []uint) map[uint]*biz.User {
|
||||
result := make(map[uint]*biz.User)
|
||||
func auditUsers(ctx context.Context, db *gorm.DB, ids []uint) map[uint]*system.User {
|
||||
result := make(map[uint]*system.User)
|
||||
if len(ids) == 0 {
|
||||
return result
|
||||
}
|
||||
@@ -87,11 +87,11 @@ func auditUsers(ctx context.Context, db *gorm.DB, ids []uint) map[uint]*biz.User
|
||||
return result
|
||||
}
|
||||
for _, value := range users {
|
||||
result[value.ID] = &biz.User{ID: value.ID, CreatedAt: value.CreatedAt, UpdatedAt: value.UpdatedAt, UUID: value.UUID, Username: value.Username, NickName: value.NickName, HeaderImg: value.HeaderImg, AuthorityID: value.AuthorityID, DeptID: value.DeptID, Phone: value.Phone, Email: value.Email, Enable: value.Enable}
|
||||
result[value.ID] = &system.User{ID: value.ID, CreatedAt: value.CreatedAt, UpdatedAt: value.UpdatedAt, UUID: value.UUID, Username: value.Username, NickName: value.NickName, HeaderImg: value.HeaderImg, AuthorityID: value.AuthorityID, DeptID: value.DeptID, Phone: value.Phone, Email: value.Email, Enable: value.Enable}
|
||||
}
|
||||
return result
|
||||
}
|
||||
func (r *auditQueryRepo) FindOperation(ctx context.Context, id uint) (*biz.OperationRecord, error) {
|
||||
func (r *auditQueryRepo) FindOperation(ctx context.Context, id uint) (*system.OperationRecord, error) {
|
||||
var po operationPO
|
||||
if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -2,9 +2,9 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz/system"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/pkg/database/pagination"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
type parameterRepo struct{ data Provider }
|
||||
|
||||
func NewParameterRepo(data Provider) biz.ParameterRepo { return ¶meterRepo{data: data} }
|
||||
func NewParameterRepo(data Provider) system.ParameterRepo { return ¶meterRepo{data: data} }
|
||||
|
||||
type parameterPO struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
@@ -28,7 +28,7 @@ type parameterPO struct {
|
||||
|
||||
func (parameterPO) TableName() string { return "sys_params" }
|
||||
|
||||
func (r *parameterRepo) CreateParameter(ctx context.Context, v *biz.SystemParameter) error {
|
||||
func (r *parameterRepo) CreateParameter(ctx context.Context, v *system.SystemParameter) error {
|
||||
po := parameterPO{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, Name: v.Name, Key: v.Key, Value: v.Value, Desc: v.Desc}
|
||||
if err := r.data.DB().WithContext(ctx).Create(&po).Error; err != nil {
|
||||
return err
|
||||
@@ -36,7 +36,7 @@ func (r *parameterRepo) CreateParameter(ctx context.Context, v *biz.SystemParame
|
||||
v.ID, v.CreatedAt, v.UpdatedAt = po.ID, po.CreatedAt, po.UpdatedAt
|
||||
return nil
|
||||
}
|
||||
func (r *parameterRepo) UpdateParameter(ctx context.Context, v *biz.SystemParameter) error {
|
||||
func (r *parameterRepo) UpdateParameter(ctx context.Context, v *system.SystemParameter) error {
|
||||
// The compatible update uses a struct, so zero-value optional fields (notably desc)
|
||||
// are intentionally ignored rather than clearing an existing value.
|
||||
return r.data.DB().WithContext(ctx).Model(¶meterPO{}).Where("id = ?", v.ID).Updates(¶meterPO{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, Name: v.Name, Key: v.Key, Value: v.Value, Desc: v.Desc}).Error
|
||||
@@ -47,21 +47,21 @@ func (r *parameterRepo) DeleteParameters(ctx context.Context, ids []string) erro
|
||||
}
|
||||
return r.data.DB().WithContext(ctx).Delete(&[]parameterPO{}, "id in ?", ids).Error
|
||||
}
|
||||
func (r *parameterRepo) FindParameterByID(ctx context.Context, id string) (*biz.SystemParameter, error) {
|
||||
func (r *parameterRepo) FindParameterByID(ctx context.Context, id string) (*system.SystemParameter, error) {
|
||||
var po parameterPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("id = ?", id).First(&po).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parameterFromPO(po), nil
|
||||
}
|
||||
func (r *parameterRepo) FindParameterByKey(ctx context.Context, key string) (*biz.SystemParameter, error) {
|
||||
func (r *parameterRepo) FindParameterByKey(ctx context.Context, key string) (*system.SystemParameter, error) {
|
||||
var po parameterPO
|
||||
if err := r.data.DB().WithContext(ctx).Where(parameterPO{Key: key}).First(&po).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parameterFromPO(po), nil
|
||||
}
|
||||
func (r *parameterRepo) ListParameters(ctx context.Context, page, size int, q *biz.SystemParameter) ([]*biz.SystemParameter, int64, error) {
|
||||
func (r *parameterRepo) ListParameters(ctx context.Context, page, size int, q *system.SystemParameter) ([]*system.SystemParameter, int64, error) {
|
||||
db := r.data.DB().WithContext(ctx).Model(¶meterPO{})
|
||||
if q != nil {
|
||||
if q.StartCreatedAt != nil && q.EndCreatedAt != nil {
|
||||
@@ -82,7 +82,7 @@ func (r *parameterRepo) ListParameters(ctx context.Context, page, size int, q *b
|
||||
if err := pagination.Apply(db, page, size, 100).Find(&pos).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]*biz.SystemParameter, 0, len(pos))
|
||||
out := make([]*system.SystemParameter, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
out = append(out, parameterFromPO(po))
|
||||
}
|
||||
|
||||
@@ -2,17 +2,16 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz/system"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
)
|
||||
|
||||
func TestParameterRepositoryPreservesModelMetadata(t *testing.T) {
|
||||
data := newTransactionTestData(t)
|
||||
repo := ¶meterRepo{data: data}
|
||||
createdAt := time.Date(2025, time.January, 2, 3, 4, 5, 0, time.Local)
|
||||
value := &biz.SystemParameter{ID: 41, CreatedAt: createdAt, Name: "name", Key: "key", Value: "value"}
|
||||
value := &system.SystemParameter{ID: 41, CreatedAt: createdAt, Name: "name", Key: "key", Value: "value"}
|
||||
|
||||
if err := repo.CreateParameter(context.Background(), value); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -40,7 +39,7 @@ func TestParameterRepositoryKeepsReferenceEmptyQuerySemantics(t *testing.T) {
|
||||
data := newTransactionTestData(t)
|
||||
repo := ¶meterRepo{data: data}
|
||||
ctx := context.Background()
|
||||
first := &biz.SystemParameter{Name: "first", Key: "first", Value: "1"}
|
||||
first := &system.SystemParameter{Name: "first", Key: "first", Value: "1"}
|
||||
if err := repo.CreateParameter(ctx, first); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -3,16 +3,15 @@ package system
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"kra/internal/biz/system"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type permissionRepo struct{ data Provider }
|
||||
|
||||
func NewPermissionRepo(data Provider) biz.PermissionRepo { return &permissionRepo{data: data} }
|
||||
func NewPermissionRepo(data Provider) system.PermissionRepo { return &permissionRepo{data: data} }
|
||||
|
||||
type menuButtonPO struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
@@ -34,12 +33,12 @@ type authorityButtonPO struct {
|
||||
|
||||
func (authorityButtonPO) TableName() string { return "sys_authority_btns" }
|
||||
|
||||
func (r *permissionRepo) Buttons(ctx context.Context, menuID uint) ([]*biz.MenuButton, error) {
|
||||
func (r *permissionRepo) Buttons(ctx context.Context, menuID uint) ([]*system.MenuButton, error) {
|
||||
var pos []menuButtonPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("sys_base_menu_id = ?", menuID).Find(&pos).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]*biz.MenuButton, 0, len(pos))
|
||||
out := make([]*system.MenuButton, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
out = append(out, menuButtonFromPO(po))
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz/system"
|
||||
"testing"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/conf"
|
||||
)
|
||||
|
||||
@@ -108,7 +108,7 @@ func TestSetSelectedButtonsStrictRequiresManagedRoleAndOwnedButton(t *testing.T)
|
||||
if err := db.Create(&authorityButtonPO{AuthorityID: actorID, MenuID: 10, ButtonID: 31}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{AuthorityID: actorID})
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{AuthorityID: actorID})
|
||||
repo := &permissionRepo{data: data}
|
||||
if err := repo.SetSelectedButtons(ctx, siblingID, 10, []uint{31}); err == nil {
|
||||
t.Fatal("SetSelectedButtons() accepted an out-of-scope authority")
|
||||
|
||||
@@ -3,16 +3,15 @@ package system
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"kra/internal/biz/system"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type positionRepo struct{ data Provider }
|
||||
|
||||
func NewPositionRepo(data Provider) biz.PositionRepo { return &positionRepo{data: data} }
|
||||
func NewPositionRepo(data Provider) system.PositionRepo { return &positionRepo{data: data} }
|
||||
|
||||
type positionPO struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
@@ -37,13 +36,13 @@ type userPositionPO struct {
|
||||
|
||||
func (userPositionPO) TableName() string { return "sys_user_positions" }
|
||||
|
||||
func posFromPO(po positionPO) *biz.Position {
|
||||
return &biz.Position{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Name: po.Name, Code: po.Code, Sort: po.Sort, Status: po.Status, Remark: po.Remark}
|
||||
func posFromPO(po positionPO) *system.Position {
|
||||
return &system.Position{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Name: po.Name, Code: po.Code, Sort: po.Sort, Status: po.Status, Remark: po.Remark}
|
||||
}
|
||||
func (r *positionRepo) CreatePosition(ctx context.Context, v *biz.Position) error {
|
||||
func (r *positionRepo) CreatePosition(ctx context.Context, v *system.Position) error {
|
||||
return r.data.DB().WithContext(ctx).Create(&positionPO{Name: v.Name, Code: v.Code, Sort: v.Sort, Status: v.Status, Remark: v.Remark}).Error
|
||||
}
|
||||
func (r *positionRepo) UpdatePosition(ctx context.Context, v *biz.Position) error {
|
||||
func (r *positionRepo) UpdatePosition(ctx context.Context, v *system.Position) error {
|
||||
return r.data.DB().WithContext(ctx).Model(&positionPO{}).Where("id = ?", v.ID).Updates(map[string]any{"name": v.Name, "code": v.Code, "sort": v.Sort, "status": v.Status, "remark": v.Remark}).Error
|
||||
}
|
||||
func (r *positionRepo) DeletePosition(ctx context.Context, id uint) error {
|
||||
@@ -59,14 +58,14 @@ func (r *positionRepo) DeletePosition(ctx context.Context, id uint) error {
|
||||
}
|
||||
return r.data.DB().WithContext(ctx).Delete(&positionPO{}, id).Error
|
||||
}
|
||||
func (r *positionRepo) FindPosition(ctx context.Context, id uint) (*biz.Position, error) {
|
||||
func (r *positionRepo) FindPosition(ctx context.Context, id uint) (*system.Position, error) {
|
||||
var po positionPO
|
||||
if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return posFromPO(po), nil
|
||||
}
|
||||
func (r *positionRepo) ListPositions(ctx context.Context, page, size int, q *biz.PositionListFilter) ([]*biz.Position, int64, error) {
|
||||
func (r *positionRepo) ListPositions(ctx context.Context, page, size int, q *system.PositionListFilter) ([]*system.Position, int64, error) {
|
||||
db := r.data.DB().WithContext(ctx).Model(&positionPO{})
|
||||
if q != nil {
|
||||
if q.Name != "" {
|
||||
@@ -90,7 +89,7 @@ func (r *positionRepo) ListPositions(ctx context.Context, page, size int, q *biz
|
||||
if err := db.Order("sort").Limit(size).Offset(size * (page - 1)).Find(&pos).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]*biz.Position, 0, len(pos))
|
||||
out := make([]*system.Position, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
out = append(out, posFromPO(po))
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ package system
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"kra/internal/biz/system"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/conf"
|
||||
"kra/internal/security"
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
type runtimeSettings struct{ runtime *conf.Runtime }
|
||||
|
||||
func NewRuntimeSettings(runtime *conf.Runtime) biz.RuntimeSettings {
|
||||
func NewRuntimeSettings(runtime *conf.Runtime) system.RuntimeSettings {
|
||||
return &runtimeSettings{runtime: runtime}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ func (s *runtimeSettings) RouterPrefix() string {
|
||||
return config.RouterPrefix
|
||||
}
|
||||
|
||||
func (s *runtimeSettings) JWTSettings() biz.JWTSettings {
|
||||
value := biz.JWTSettings{Issuer: "kra", Expires: 7 * 24 * time.Hour, Buffer: 24 * time.Hour}
|
||||
func (s *runtimeSettings) JWTSettings() system.JWTSettings {
|
||||
value := system.JWTSettings{Issuer: "kra", Expires: 7 * 24 * time.Hour, Buffer: 24 * time.Hour}
|
||||
config := s.runtime.Admin()
|
||||
if config == nil || config.Jwt == nil {
|
||||
return value
|
||||
@@ -46,8 +46,8 @@ func (s *runtimeSettings) JWTSettings() biz.JWTSettings {
|
||||
return value
|
||||
}
|
||||
|
||||
func (s *runtimeSettings) CaptchaSettings() biz.CaptchaSettings {
|
||||
value := biz.CaptchaSettings{KeyLong: 6, ImageWidth: 240, ImageHeight: 80, StoreExpiration: 3 * time.Minute}
|
||||
func (s *runtimeSettings) CaptchaSettings() system.CaptchaSettings {
|
||||
value := system.CaptchaSettings{KeyLong: 6, ImageWidth: 240, ImageHeight: 80, StoreExpiration: 3 * time.Minute}
|
||||
config := s.runtime.Admin()
|
||||
if config == nil || config.Captcha == nil {
|
||||
return value
|
||||
@@ -67,12 +67,12 @@ func (s *runtimeSettings) CaptchaSettings() biz.CaptchaSettings {
|
||||
return value
|
||||
}
|
||||
|
||||
func (s *runtimeSettings) MediaSettings() biz.MediaSettings {
|
||||
func (s *runtimeSettings) MediaSettings() system.MediaSettings {
|
||||
config := s.runtime.Admin()
|
||||
if config == nil || config.Media == nil {
|
||||
return biz.MediaSettings{}
|
||||
return system.MediaSettings{}
|
||||
}
|
||||
return biz.MediaSettings{SessionTTL: int(config.Media.SessionTtl), MaxFileSize: config.Media.MaxFileSize, ChunkDir: config.Media.ChunkDir}
|
||||
return system.MediaSettings{SessionTTL: int(config.Media.SessionTtl), MaxFileSize: config.Media.MaxFileSize, ChunkDir: config.Media.ChunkDir}
|
||||
}
|
||||
|
||||
func (s *runtimeSettings) UseMultipoint() bool {
|
||||
@@ -80,13 +80,13 @@ func (s *runtimeSettings) UseMultipoint() bool {
|
||||
return config != nil && config.System != nil && config.System.UseMultipoint
|
||||
}
|
||||
|
||||
type tokenIssuer struct{ settings biz.RuntimeSettings }
|
||||
type tokenIssuer struct{ settings system.RuntimeSettings }
|
||||
|
||||
func NewTokenIssuer(settings biz.RuntimeSettings) biz.TokenIssuer {
|
||||
func NewTokenIssuer(settings system.RuntimeSettings) system.TokenIssuer {
|
||||
return &tokenIssuer{settings: settings}
|
||||
}
|
||||
|
||||
func (i *tokenIssuer) IssueToken(user *biz.User, authorityID uint, mustChangePassword bool, expires time.Duration) (*biz.IssuedToken, error) {
|
||||
func (i *tokenIssuer) IssueToken(user *system.User, authorityID uint, mustChangePassword bool, expires time.Duration) (*system.IssuedToken, error) {
|
||||
settings := i.settings.JWTSettings()
|
||||
if expires <= 0 {
|
||||
expires = settings.Expires
|
||||
@@ -99,10 +99,10 @@ func (i *tokenIssuer) IssueToken(user *biz.User, authorityID uint, mustChangePas
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &biz.IssuedToken{Value: token, ExpiresAt: claims.ExpiresAt.Time, TTL: expires}, nil
|
||||
return &system.IssuedToken{Value: token, ExpiresAt: claims.ExpiresAt.Time, TTL: expires}, nil
|
||||
}
|
||||
|
||||
func (i *tokenIssuer) ReissueToken(source *biz.AuthClaims, authorityID uint) (*biz.IssuedToken, error) {
|
||||
func (i *tokenIssuer) ReissueToken(source *system.AuthClaims, authorityID uint) (*system.IssuedToken, error) {
|
||||
if source == nil {
|
||||
return nil, errors.New("nil JWT claims")
|
||||
}
|
||||
@@ -120,23 +120,23 @@ func (i *tokenIssuer) ReissueToken(source *biz.AuthClaims, authorityID uint) (*b
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &biz.IssuedToken{Value: token, ExpiresAt: source.ExpiresAt, TTL: time.Until(source.ExpiresAt)}, nil
|
||||
return &system.IssuedToken{Value: token, ExpiresAt: source.ExpiresAt, TTL: time.Until(source.ExpiresAt)}, nil
|
||||
}
|
||||
|
||||
func (i *tokenIssuer) ParseToken(token string) (*biz.AuthClaims, error) {
|
||||
func (i *tokenIssuer) ParseToken(token string) (*system.AuthClaims, error) {
|
||||
claims, err := security.Parse(token, i.settings.JWTSettings().SigningKey)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, security.ErrTokenExpired):
|
||||
return nil, biz.ErrTokenExpired
|
||||
return nil, system.ErrTokenExpired
|
||||
case errors.Is(err, security.ErrTokenMalformed):
|
||||
return nil, biz.ErrTokenMalformed
|
||||
return nil, system.ErrTokenMalformed
|
||||
case errors.Is(err, security.ErrTokenSignatureInvalid):
|
||||
return nil, biz.ErrTokenSignatureInvalid
|
||||
return nil, system.ErrTokenSignatureInvalid
|
||||
case errors.Is(err, security.ErrTokenNotValidYet):
|
||||
return nil, biz.ErrTokenNotValidYet
|
||||
return nil, system.ErrTokenNotValidYet
|
||||
default:
|
||||
return nil, biz.ErrTokenInvalid
|
||||
return nil, system.ErrTokenInvalid
|
||||
}
|
||||
}
|
||||
audience := append([]string(nil), claims.Audience...)
|
||||
@@ -144,5 +144,5 @@ func (i *tokenIssuer) ParseToken(token string) (*biz.AuthClaims, error) {
|
||||
if claims.IssuedAt != nil {
|
||||
issuedAt = claims.IssuedAt.Time
|
||||
}
|
||||
return &biz.AuthClaims{UUID: claims.UUID, ID: claims.ID, Username: claims.Username, NickName: claims.NickName, AuthorityID: claims.AuthorityID, UserType: claims.UserType, BufferTime: time.Duration(claims.BufferTime) * time.Second, MustChangePwd: claims.MustChangePwd, PasswordVersion: claims.PasswordVersion, Issuer: claims.Issuer, Audience: audience, IssuedAt: issuedAt, NotBefore: claims.NotBefore.Time, ExpiresAt: claims.ExpiresAt.Time}, nil
|
||||
return &system.AuthClaims{UUID: claims.UUID, ID: claims.ID, Username: claims.Username, NickName: claims.NickName, AuthorityID: claims.AuthorityID, UserType: claims.UserType, BufferTime: time.Duration(claims.BufferTime) * time.Second, MustChangePwd: claims.MustChangePwd, PasswordVersion: claims.PasswordVersion, Issuer: claims.Issuer, Audience: audience, IssuedAt: issuedAt, NotBefore: claims.NotBefore.Time, ExpiresAt: claims.ExpiresAt.Time}, nil
|
||||
}
|
||||
|
||||
@@ -3,10 +3,9 @@ package system
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"kra/internal/biz/system"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -51,17 +50,17 @@ func DefaultSecurityConfig() SecurityConfigPO {
|
||||
|
||||
type securityRepo struct{ data DatabaseProvider }
|
||||
|
||||
func NewSecurityRepo(data DatabaseProvider) biz.SecurityRepo { return &securityRepo{data: data} }
|
||||
func NewSecurityRepo(data DatabaseProvider) system.SecurityRepo { return &securityRepo{data: data} }
|
||||
|
||||
func securityFromPO(v SecurityConfigPO) *biz.SecurityConfig {
|
||||
return &biz.SecurityConfig{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, CaptchaOpen: v.CaptchaOpen, CaptchaTimeout: v.CaptchaTimeout, KeyLong: v.KeyLong, ImgWidth: v.ImgWidth, ImgHeight: v.ImgHeight, PwdMinLength: v.PwdMinLength, PwdRequireUpper: v.PwdRequireUpper, PwdRequireLower: v.PwdRequireLower, PwdRequireDigit: v.PwdRequireDigit, PwdRequireSpecial: v.PwdRequireSpecial, LimitEnable: v.LimitEnable, LimitWindow: v.LimitWindow, LimitCount: v.LimitCount, LockEnable: v.LockEnable, LockThreshold: v.LockThreshold, LockDuration: v.LockDuration, PwdExpireEnable: v.PwdExpireEnable, PwdExpireDays: v.PwdExpireDays, ForceNewUserChangePassword: v.ForceNewUserChangePassword}
|
||||
func securityFromPO(v SecurityConfigPO) *system.SecurityConfig {
|
||||
return &system.SecurityConfig{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, CaptchaOpen: v.CaptchaOpen, CaptchaTimeout: v.CaptchaTimeout, KeyLong: v.KeyLong, ImgWidth: v.ImgWidth, ImgHeight: v.ImgHeight, PwdMinLength: v.PwdMinLength, PwdRequireUpper: v.PwdRequireUpper, PwdRequireLower: v.PwdRequireLower, PwdRequireDigit: v.PwdRequireDigit, PwdRequireSpecial: v.PwdRequireSpecial, LimitEnable: v.LimitEnable, LimitWindow: v.LimitWindow, LimitCount: v.LimitCount, LockEnable: v.LockEnable, LockThreshold: v.LockThreshold, LockDuration: v.LockDuration, PwdExpireEnable: v.PwdExpireEnable, PwdExpireDays: v.PwdExpireDays, ForceNewUserChangePassword: v.ForceNewUserChangePassword}
|
||||
}
|
||||
|
||||
func securityToPO(v *biz.SecurityConfig) SecurityConfigPO {
|
||||
func securityToPO(v *system.SecurityConfig) SecurityConfigPO {
|
||||
return SecurityConfigPO{ID: 1, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, CaptchaOpen: v.CaptchaOpen, CaptchaTimeout: v.CaptchaTimeout, KeyLong: v.KeyLong, ImgWidth: v.ImgWidth, ImgHeight: v.ImgHeight, PwdMinLength: v.PwdMinLength, PwdRequireUpper: v.PwdRequireUpper, PwdRequireLower: v.PwdRequireLower, PwdRequireDigit: v.PwdRequireDigit, PwdRequireSpecial: v.PwdRequireSpecial, LimitEnable: v.LimitEnable, LimitWindow: v.LimitWindow, LimitCount: v.LimitCount, LockEnable: v.LockEnable, LockThreshold: v.LockThreshold, LockDuration: v.LockDuration, PwdExpireEnable: v.PwdExpireEnable, PwdExpireDays: v.PwdExpireDays, ForceNewUserChangePassword: v.ForceNewUserChangePassword}
|
||||
}
|
||||
|
||||
func (r *securityRepo) SecurityConfig(ctx context.Context) (*biz.SecurityConfig, error) {
|
||||
func (r *securityRepo) SecurityConfig(ctx context.Context) (*system.SecurityConfig, error) {
|
||||
if !r.data.DatabaseReady() {
|
||||
po := DefaultSecurityConfig()
|
||||
po.ID = 0
|
||||
@@ -80,7 +79,7 @@ func (r *securityRepo) SecurityConfig(ctx context.Context) (*biz.SecurityConfig,
|
||||
return securityFromPO(po), nil
|
||||
}
|
||||
|
||||
func (r *securityRepo) SaveSecurityConfig(ctx context.Context, v *biz.SecurityConfig) error {
|
||||
func (r *securityRepo) SaveSecurityConfig(ctx context.Context, v *system.SecurityConfig) error {
|
||||
po := securityToPO(v)
|
||||
if err := r.data.DB().WithContext(ctx).Save(&po).Error; err != nil {
|
||||
return err
|
||||
|
||||
@@ -2,10 +2,10 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz/system"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
platformmodule "kra/pkg/module"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -13,14 +13,14 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func SeedSystem(ctx context.Context, db *gorm.DB, input *biz.DatabaseConfig, surfaces ...platformmodule.Surface) error {
|
||||
func SeedSystem(ctx context.Context, db *gorm.DB, input *system.DatabaseConfig, surfaces ...platformmodule.Surface) error {
|
||||
return seedSystem(ctx, db, input, nil, surfaces...)
|
||||
}
|
||||
|
||||
// SeedSystemWithCatalog applies module-contributed administration surfaces and
|
||||
// default timed tasks in one transaction. The system module remains the owner
|
||||
// of the system tables, while other modules contribute through the catalog.
|
||||
func SeedSystemWithCatalog(ctx context.Context, db *gorm.DB, input *biz.DatabaseConfig, catalog platformmodule.Catalog) error {
|
||||
func SeedSystemWithCatalog(ctx context.Context, db *gorm.DB, input *system.DatabaseConfig, catalog platformmodule.Catalog) error {
|
||||
surfaces := make([]platformmodule.Surface, 0, len(catalog.Definitions))
|
||||
for _, definition := range catalog.Definitions {
|
||||
surfaces = append(surfaces, definition.Surface)
|
||||
@@ -28,7 +28,7 @@ func SeedSystemWithCatalog(ctx context.Context, db *gorm.DB, input *biz.Database
|
||||
return seedSystem(ctx, db, input, catalog.DefaultTimedTasks(), surfaces...)
|
||||
}
|
||||
|
||||
func seedSystem(ctx context.Context, db *gorm.DB, input *biz.DatabaseConfig, defaults []platformmodule.TimedTask, surfaces ...platformmodule.Surface) error {
|
||||
func seedSystem(ctx context.Context, db *gorm.DB, input *system.DatabaseConfig, defaults []platformmodule.TimedTask, surfaces ...platformmodule.Surface) error {
|
||||
return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
rootParentID := uint(0)
|
||||
authority := authorityPO{AuthorityID: 888, AuthorityName: "超级管理员", ParentID: &rootParentID, DataScope: 1, DefaultRouter: "dashboard"}
|
||||
|
||||
@@ -2,9 +2,9 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz/system"
|
||||
"testing"
|
||||
|
||||
"kra/internal/biz"
|
||||
platformmodule "kra/pkg/module"
|
||||
)
|
||||
|
||||
@@ -20,7 +20,7 @@ func TestSeedSystemCreatesInitialDataAndModuleSurface(t *testing.T) {
|
||||
Menus: []platformmodule.Menu{{Name: "orders", Path: "orders", ParentName: "extensions", Component: "view/orders.vue", Title: "订单", Sort: 6}},
|
||||
APIs: []platformmodule.API{{Path: "/orders", Method: "GET", Group: "订单", Description: "订单列表"}},
|
||||
}
|
||||
input := &biz.DatabaseConfig{AdminPassword: "admin-password", APIs: []*biz.API{{Path: "/healthz", Method: "GET", APIGroup: "系统"}}}
|
||||
input := &system.DatabaseConfig{AdminPassword: "admin-password", APIs: []*system.API{{Path: "/healthz", Method: "GET", APIGroup: "系统"}}}
|
||||
if err = SeedSystem(context.Background(), db, input, surface); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz"
|
||||
"kra/internal/biz/system"
|
||||
"kra/pkg/database/gormkit"
|
||||
"kra/pkg/database/pagination"
|
||||
"time"
|
||||
@@ -46,14 +46,14 @@ func (taskLogPO) TableName() string { return "sys_timed_task_logs" }
|
||||
|
||||
type taskRepo struct{ data Provider }
|
||||
|
||||
func NewTaskRepo(data Provider) biz.TaskRepo { return &taskRepo{data: data} }
|
||||
func taskToPO(v *biz.TimedTask) taskPO {
|
||||
func NewTaskRepo(data Provider) system.TaskRepo { return &taskRepo{data: data} }
|
||||
func taskToPO(v *system.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}
|
||||
}
|
||||
func taskFromPO(v taskPO) *biz.TimedTask {
|
||||
return &biz.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 taskFromPO(v taskPO) *system.TimedTask {
|
||||
return &system.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 *biz.TimedTask) error {
|
||||
func (r *taskRepo) CreateTask(ctx context.Context, v *system.TimedTask) error {
|
||||
po := taskToPO(v)
|
||||
if err := r.data.DB().WithContext(ctx).Create(&po).Error; err != nil {
|
||||
return err
|
||||
@@ -70,26 +70,26 @@ func (r *taskRepo) TaskNameExists(ctx context.Context, name string, excludeID ui
|
||||
err := db.Count(&count).Error
|
||||
return count > 0, err
|
||||
}
|
||||
func (r *taskRepo) UpdateTask(ctx context.Context, v *biz.TimedTask) error {
|
||||
func (r *taskRepo) UpdateTask(ctx context.Context, v *system.TimedTask) error {
|
||||
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
|
||||
}
|
||||
func (r *taskRepo) DeleteTask(ctx context.Context, id uint) error {
|
||||
return r.data.DB().WithContext(ctx).Delete(&taskPO{}, id).Error
|
||||
}
|
||||
func (r *taskRepo) FindTask(ctx context.Context, id uint) (*biz.TimedTask, error) {
|
||||
func (r *taskRepo) FindTask(ctx context.Context, id uint) (*system.TimedTask, error) {
|
||||
var po taskPO
|
||||
if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return taskFromPO(po), nil
|
||||
}
|
||||
func (r *taskRepo) ListTasks(ctx context.Context, page, size int, q *biz.TimedTask) ([]*biz.TimedTask, int64, error) {
|
||||
func (r *taskRepo) ListTasks(ctx context.Context, page, size int, q *system.TimedTask) ([]*system.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
|
||||
return []*system.TimedTask{}, 0, nil
|
||||
}
|
||||
db := r.data.DB().WithContext(ctx).Model(&taskPO{})
|
||||
if q != nil {
|
||||
@@ -111,7 +111,7 @@ func (r *taskRepo) ListTasks(ctx context.Context, page, size int, q *biz.TimedTa
|
||||
if err := pagination.Apply(db.Order("id desc"), page, size, 100).Find(&pos).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]*biz.TimedTask, 0, len(pos))
|
||||
out := make([]*system.TimedTask, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
out = append(out, taskFromPO(po))
|
||||
}
|
||||
@@ -120,13 +120,13 @@ func (r *taskRepo) ListTasks(ctx context.Context, page, size int, q *biz.TimedTa
|
||||
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
|
||||
}
|
||||
func (r *taskRepo) RecordTaskLog(ctx context.Context, v *biz.TimedTaskLog) error {
|
||||
func (r *taskRepo) RecordTaskLog(ctx context.Context, v *system.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
|
||||
}
|
||||
func taskLogFromPO(v taskLogPO) *biz.TimedTaskLog {
|
||||
return &biz.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 taskLogFromPO(v taskLogPO) *system.TimedTaskLog {
|
||||
return &system.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) ([]*biz.TimedTaskLog, int64, error) {
|
||||
func (r *taskRepo) ListTaskLogs(ctx context.Context, page, size int, taskID uint, status string) ([]*system.TimedTaskLog, int64, error) {
|
||||
db := r.data.DB().WithContext(ctx).Model(&taskLogPO{})
|
||||
if taskID != 0 {
|
||||
db = db.Where("task_id = ?", taskID)
|
||||
@@ -142,7 +142,7 @@ func (r *taskRepo) ListTaskLogs(ctx context.Context, page, size int, taskID uint
|
||||
if err := pagination.Apply(db.Order("id desc"), page, size, 100).Find(&pos).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]*biz.TimedTaskLog, 0, len(pos))
|
||||
out := make([]*system.TimedTaskLog, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
out = append(out, taskLogFromPO(po))
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz/system"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/conf"
|
||||
)
|
||||
|
||||
@@ -31,7 +31,7 @@ func TestUserAuthorityWritesAreAtomic(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
repo := &userRepo{data: data}
|
||||
user := &biz.User{Username: "atomic", Password: "hash", NickName: "before", AuthorityID: 888, Enable: 1}
|
||||
user := &system.User{Username: "atomic", Password: "hash", NickName: "before", AuthorityID: 888, Enable: 1}
|
||||
created, err := repo.CreateUserWithAuthorities(ctx, user, []uint{888, 999})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -61,7 +61,7 @@ func TestUserAuthorityWritesAreAtomic(t *testing.T) {
|
||||
t.Fatalf("missing-authority link count = %d, err = %v", missingAuthorityLinks, err)
|
||||
}
|
||||
|
||||
withNewAuthority := &biz.User{Username: "with-new-authority", Password: "hash", AuthorityID: 888, Enable: 1}
|
||||
withNewAuthority := &system.User{Username: "with-new-authority", Password: "hash", AuthorityID: 888, Enable: 1}
|
||||
createdWithNewAuthority, err := repo.CreateUserWithAuthorities(ctx, withNewAuthority, []uint{888, 123456})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -116,7 +116,7 @@ func TestUserRegistrationDoesNotAddPrimaryAuthorityToRequestedAssociations(t *te
|
||||
if err := data.gormDB.WithContext(ctx).Create(&[]authorityPO{{AuthorityID: 888, AuthorityName: "primary"}, {AuthorityID: 999, AuthorityName: "requested"}}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
created, err := (&userRepo{data: data}).CreateUserWithAuthorities(ctx, &biz.User{Username: "association-shape", Password: "hash", AuthorityID: 888, Enable: 1}, []uint{999})
|
||||
created, err := (&userRepo{data: data}).CreateUserWithAuthorities(ctx, &system.User{Username: "association-shape", Password: "hash", AuthorityID: 888, Enable: 1}, []uint{999})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -173,8 +173,8 @@ func TestDictionaryImportKeepsHierarchyInOneTransaction(t *testing.T) {
|
||||
repo := &dictionaryRepo{data: data}
|
||||
parentID := uint(10)
|
||||
active := true
|
||||
dictionary := &biz.Dictionary{Name: "status", Type: "status", Status: &active}
|
||||
details := []*biz.DictionaryDetail{{ID: 10, Label: "parent", Value: "1", Status: &active}, {ID: 11, Label: "child", Value: "2", ParentID: &parentID, Status: &active}}
|
||||
dictionary := &system.Dictionary{Name: "status", Type: "status", Status: &active}
|
||||
details := []*system.DictionaryDetail{{ID: 10, Label: "parent", Value: "1", Status: &active}, {ID: 11, Label: "child", Value: "2", ParentID: &parentID, Status: &active}}
|
||||
if err := repo.ImportDictionary(ctx, dictionary, details); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"kra/internal/biz/system"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/pkg/database/pagination"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -16,40 +16,40 @@ import (
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
func (r *userRepo) FindUserByUsername(ctx context.Context, username string) (*biz.User, error) {
|
||||
func (r *userRepo) FindUserByUsername(ctx context.Context, username string) (*system.User, error) {
|
||||
var po userPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("username = ?", username).First(&po).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, biz.ErrUserNotFound
|
||||
return nil, system.ErrUserNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return r.loadUser(ctx, &po)
|
||||
}
|
||||
|
||||
func (r *userRepo) FindUserByID(ctx context.Context, id uint) (*biz.User, error) {
|
||||
func (r *userRepo) FindUserByID(ctx context.Context, id uint) (*system.User, error) {
|
||||
var po userPO
|
||||
if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, biz.ErrUserNotFound
|
||||
return nil, system.ErrUserNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return r.loadUser(ctx, &po)
|
||||
}
|
||||
|
||||
func (r *userRepo) FindUserByUUID(ctx context.Context, value string) (*biz.User, error) {
|
||||
func (r *userRepo) FindUserByUUID(ctx context.Context, value string) (*system.User, error) {
|
||||
var po userPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("uuid = ?", value).First(&po).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, biz.ErrUserNotFound
|
||||
return nil, system.ErrUserNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return r.loadUser(ctx, &po)
|
||||
}
|
||||
|
||||
func (r *userRepo) loadUser(ctx context.Context, po *userPO) (*biz.User, error) {
|
||||
func (r *userRepo) loadUser(ctx context.Context, po *userPO) (*system.User, error) {
|
||||
var authority authorityPO
|
||||
if err := r.data.DB().WithContext(ctx).First(&authority, "authority_id = ?", po.AuthorityID).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, err
|
||||
@@ -58,7 +58,7 @@ func (r *userRepo) loadUser(ctx context.Context, po *userPO) (*biz.User, error)
|
||||
if err := r.data.DB().WithContext(ctx).Table("sys_authorities").Joins("JOIN sys_user_authority ON sys_user_authority.sys_authority_authority_id = sys_authorities.authority_id").Where("sys_user_authority.sys_user_id = ? AND sys_authorities.deleted_at IS NULL", po.ID).Find(&authorityPOs).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorities := make([]biz.Authority, 0, len(authorityPOs))
|
||||
authorities := make([]system.Authority, 0, len(authorityPOs))
|
||||
for _, item := range authorityPOs {
|
||||
authorities = append(authorities, toBizAuthority(item))
|
||||
}
|
||||
@@ -70,12 +70,12 @@ func (r *userRepo) loadUser(ctx context.Context, po *userPO) (*biz.User, error)
|
||||
if err := r.data.DB().WithContext(ctx).Table("sys_departments").Joins("JOIN sys_user_departments ON sys_user_departments.sys_department_id = sys_departments.id").Where("sys_user_departments.sys_user_id = ? AND sys_departments.deleted_at IS NULL", po.ID).Find(&departmentPOs).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
departments := make([]biz.Department, 0, len(departmentPOs))
|
||||
departments := make([]system.Department, 0, len(departmentPOs))
|
||||
for _, item := range departmentPOs {
|
||||
value := deptFromPO(item)
|
||||
departments = append(departments, *value)
|
||||
}
|
||||
var primary *biz.Department
|
||||
var primary *system.Department
|
||||
if po.DeptID != 0 {
|
||||
var primaryPO departmentPO
|
||||
if err := r.data.DB().WithContext(ctx).First(&primaryPO, po.DeptID).Error; err == nil {
|
||||
@@ -88,33 +88,33 @@ func (r *userRepo) loadUser(ctx context.Context, po *userPO) (*biz.User, error)
|
||||
if err := r.data.DB().WithContext(ctx).Table("sys_positions").Joins("JOIN sys_user_positions ON sys_user_positions.sys_position_id = sys_positions.id").Where("sys_user_positions.sys_user_id = ? AND sys_positions.deleted_at IS NULL", po.ID).Find(&positionPOs).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
positions := make([]biz.Position, 0, len(positionPOs))
|
||||
positions := make([]system.Position, 0, len(positionPOs))
|
||||
for _, item := range positionPOs {
|
||||
positions = append(positions, *posFromPO(item))
|
||||
}
|
||||
return &biz.User{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, UUID: po.UUID, Username: po.Username, Password: po.Password, NickName: po.NickName, HeaderImg: po.HeaderImg, AuthorityID: po.AuthorityID, Authority: toBizAuthority(authority), Authorities: authorities, DeptID: po.DeptID, Department: primary, Departments: departments, Positions: positions, Phone: po.Phone, Email: po.Email, Enable: po.Enable, OriginSetting: setting, MustChangePassword: po.MustChangePassword, PasswordUpdatedAt: po.PasswordUpdatedAt}, nil
|
||||
return &system.User{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, UUID: po.UUID, Username: po.Username, Password: po.Password, NickName: po.NickName, HeaderImg: po.HeaderImg, AuthorityID: po.AuthorityID, Authority: toBizAuthority(authority), Authorities: authorities, DeptID: po.DeptID, Department: primary, Departments: departments, Positions: positions, Phone: po.Phone, Email: po.Email, Enable: po.Enable, OriginSetting: setting, MustChangePassword: po.MustChangePassword, PasswordUpdatedAt: po.PasswordUpdatedAt}, nil
|
||||
}
|
||||
|
||||
func toBizAuthority(po authorityPO) biz.Authority {
|
||||
func toBizAuthority(po authorityPO) system.Authority {
|
||||
var deletedAt *time.Time
|
||||
if po.DeletedAt.Valid {
|
||||
value := po.DeletedAt.Time
|
||||
deletedAt = &value
|
||||
}
|
||||
return biz.Authority{CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, DeletedAt: deletedAt, AuthorityID: po.AuthorityID, AuthorityName: po.AuthorityName, ParentID: po.ParentID, DataScope: po.DataScope, DefaultRouter: po.DefaultRouter}
|
||||
return system.Authority{CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, DeletedAt: deletedAt, AuthorityID: po.AuthorityID, AuthorityName: po.AuthorityName, ParentID: po.ParentID, DataScope: po.DataScope, DefaultRouter: po.DefaultRouter}
|
||||
}
|
||||
|
||||
func baseBizUser(po *userPO) *biz.User {
|
||||
func baseBizUser(po *userPO) *system.User {
|
||||
setting := map[string]any(nil)
|
||||
if po.OriginSetting != "" {
|
||||
_ = json.Unmarshal([]byte(po.OriginSetting), &setting)
|
||||
}
|
||||
return &biz.User{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, UUID: po.UUID, Username: po.Username, Password: po.Password, NickName: po.NickName, HeaderImg: po.HeaderImg, AuthorityID: po.AuthorityID, DeptID: po.DeptID, Phone: po.Phone, Email: po.Email, Enable: po.Enable, OriginSetting: setting, MustChangePassword: po.MustChangePassword, PasswordUpdatedAt: po.PasswordUpdatedAt}
|
||||
return &system.User{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, UUID: po.UUID, Username: po.Username, Password: po.Password, NickName: po.NickName, HeaderImg: po.HeaderImg, AuthorityID: po.AuthorityID, DeptID: po.DeptID, Phone: po.Phone, Email: po.Email, Enable: po.Enable, OriginSetting: setting, MustChangePassword: po.MustChangePassword, PasswordUpdatedAt: po.PasswordUpdatedAt}
|
||||
}
|
||||
|
||||
func (r *userRepo) loadUsers(ctx context.Context, pos []userPO) ([]*biz.User, error) {
|
||||
func (r *userRepo) loadUsers(ctx context.Context, pos []userPO) ([]*system.User, error) {
|
||||
if len(pos) == 0 {
|
||||
return []*biz.User{}, nil
|
||||
return []*system.User{}, nil
|
||||
}
|
||||
db := r.data.DB().WithContext(ctx)
|
||||
userIDs := make([]uint, 0, len(pos))
|
||||
@@ -186,18 +186,18 @@ func (r *userRepo) loadUsers(ctx context.Context, pos []userPO) ([]*biz.User, er
|
||||
positionByID[item.ID] = item
|
||||
}
|
||||
|
||||
users := make([]*biz.User, 0, len(pos))
|
||||
users := make([]*system.User, 0, len(pos))
|
||||
for i := range pos {
|
||||
po := &pos[i]
|
||||
user := baseBizUser(po)
|
||||
user.Authority = toBizAuthority(authorityByID[po.AuthorityID])
|
||||
user.Authorities = make([]biz.Authority, 0, len(authorityByUser[po.ID]))
|
||||
user.Authorities = make([]system.Authority, 0, len(authorityByUser[po.ID]))
|
||||
for _, id := range authorityByUser[po.ID] {
|
||||
if item, ok := authorityByID[id]; ok {
|
||||
user.Authorities = append(user.Authorities, toBizAuthority(item))
|
||||
}
|
||||
}
|
||||
user.Departments = make([]biz.Department, 0, len(departmentByUser[po.ID]))
|
||||
user.Departments = make([]system.Department, 0, len(departmentByUser[po.ID]))
|
||||
for _, id := range departmentByUser[po.ID] {
|
||||
if item, ok := departmentByID[id]; ok {
|
||||
user.Departments = append(user.Departments, *deptFromPO(item))
|
||||
@@ -206,7 +206,7 @@ func (r *userRepo) loadUsers(ctx context.Context, pos []userPO) ([]*biz.User, er
|
||||
if primary, ok := departmentByID[po.DeptID]; ok {
|
||||
user.Department = deptFromPO(primary)
|
||||
}
|
||||
user.Positions = make([]biz.Position, 0, len(positionByUser[po.ID]))
|
||||
user.Positions = make([]system.Position, 0, len(positionByUser[po.ID]))
|
||||
for _, id := range positionByUser[po.ID] {
|
||||
if item, ok := positionByID[id]; ok {
|
||||
user.Positions = append(user.Positions, *posFromPO(item))
|
||||
@@ -226,7 +226,7 @@ func (r *userRepo) HasAuthorityMenu(ctx context.Context, authorityID uint, name
|
||||
return count > 0, err
|
||||
}
|
||||
|
||||
func (r *userRepo) MenusByAuthority(ctx context.Context, authorityID uint) ([]*biz.Menu, error) {
|
||||
func (r *userRepo) MenusByAuthority(ctx context.Context, authorityID uint) ([]*system.Menu, error) {
|
||||
var pos []menuPO
|
||||
err := r.data.DB().WithContext(ctx).Table("sys_base_menus").
|
||||
Joins("JOIN sys_authority_menus ON sys_authority_menus.sys_base_menu_id = sys_base_menus.id").
|
||||
@@ -235,13 +235,13 @@ func (r *userRepo) MenusByAuthority(ctx context.Context, authorityID uint) ([]*b
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
byID := make(map[uint]*biz.Menu, len(pos))
|
||||
byID := make(map[uint]*system.Menu, len(pos))
|
||||
menuIDs := make([]uint, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
menu := menuFromPO(po)
|
||||
menu.MenuID = po.ID
|
||||
menu.Children = nil
|
||||
menu.Parameters = []*biz.MenuParameter{}
|
||||
menu.Parameters = []*system.MenuParameter{}
|
||||
menu.Buttons = nil
|
||||
byID[po.ID] = menu
|
||||
menuIDs = append(menuIDs, po.ID)
|
||||
@@ -266,7 +266,7 @@ func (r *userRepo) MenusByAuthority(ctx context.Context, authorityID uint) ([]*b
|
||||
menu.Buttons = append(menu.Buttons, value)
|
||||
}
|
||||
}
|
||||
var roots []*biz.Menu
|
||||
var roots []*system.Menu
|
||||
for _, po := range pos {
|
||||
menu := byID[po.ID]
|
||||
if parent := byID[po.ParentID]; parent != nil {
|
||||
@@ -278,7 +278,7 @@ func (r *userRepo) MenusByAuthority(ctx context.Context, authorityID uint) ([]*b
|
||||
return roots, nil
|
||||
}
|
||||
|
||||
func (r *userRepo) ListUsers(ctx context.Context, page, pageSize int, filter *biz.UserListFilter) ([]*biz.User, int64, error) {
|
||||
func (r *userRepo) ListUsers(ctx context.Context, page, pageSize int, filter *system.UserListFilter) ([]*system.User, int64, error) {
|
||||
db := r.data.DB().WithContext(ctx).Model(&userPO{})
|
||||
order := "id desc"
|
||||
if filter != nil {
|
||||
@@ -314,11 +314,11 @@ func (r *userRepo) ListUsers(ctx context.Context, page, pageSize int, filter *bi
|
||||
return users, total, err
|
||||
}
|
||||
|
||||
func (r *userRepo) CreateUser(ctx context.Context, user *biz.User) (*biz.User, error) {
|
||||
func (r *userRepo) CreateUser(ctx context.Context, user *system.User) (*system.User, error) {
|
||||
return r.CreateUserWithAuthorities(ctx, user, nil)
|
||||
}
|
||||
|
||||
func (r *userRepo) CreateUserWithAuthorities(ctx context.Context, user *biz.User, authorityIDs []uint) (*biz.User, error) {
|
||||
func (r *userRepo) CreateUserWithAuthorities(ctx context.Context, user *system.User, authorityIDs []uint) (*system.User, error) {
|
||||
requestedAuthorities := append([]uint{user.AuthorityID}, authorityIDs...)
|
||||
if err := (&authorityAccessRepo{data: r.data}).checkAuthorityIDsAuth(ctx, requestedAuthorities); err != nil {
|
||||
return nil, err
|
||||
@@ -337,7 +337,7 @@ func (r *userRepo) CreateUserWithAuthorities(ctx context.Context, user *biz.User
|
||||
}
|
||||
now := time.Now()
|
||||
po := userPO{UUID: user.UUID, Username: user.Username, Password: user.Password, NickName: user.NickName, HeaderImg: user.HeaderImg, AuthorityID: user.AuthorityID, Phone: user.Phone, Email: user.Email, Enable: user.Enable, PasswordUpdatedAt: &now, MustChangePassword: user.MustChangePassword}
|
||||
var createdAuthorities []biz.Authority
|
||||
var createdAuthorities []system.Authority
|
||||
if err := r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
// The compatible registration rejects an existing non-deleted username before creating the
|
||||
// account. The username column is intentionally not unique for legacy
|
||||
@@ -381,14 +381,14 @@ func (r *userRepo) CreateUserWithAuthorities(ctx context.Context, user *biz.User
|
||||
return created, nil
|
||||
}
|
||||
|
||||
func (r *userRepo) UpdateUser(ctx context.Context, user *biz.User) error {
|
||||
func (r *userRepo) UpdateUser(ctx context.Context, user *system.User) error {
|
||||
if err := r.checkUserUpdateAuth(ctx, user); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.updateUser(r.data.DB().WithContext(ctx), user)
|
||||
}
|
||||
|
||||
func (r *userRepo) checkUserUpdateAuth(ctx context.Context, user *biz.User) error {
|
||||
func (r *userRepo) checkUserUpdateAuth(ctx context.Context, user *system.User) error {
|
||||
access := &authorityAccessRepo{data: r.data}
|
||||
if err := access.checkUserIDAuth(ctx, user.ID, false); err != nil {
|
||||
return err
|
||||
@@ -399,7 +399,7 @@ func (r *userRepo) checkUserUpdateAuth(ctx context.Context, user *biz.User) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *userRepo) UpdateSelfUser(ctx context.Context, user *biz.User) error {
|
||||
func (r *userRepo) UpdateSelfUser(ctx context.Context, user *system.User) error {
|
||||
updates := make(map[string]any)
|
||||
if user.NickName != "" {
|
||||
updates["nick_name"] = user.NickName
|
||||
@@ -422,7 +422,7 @@ func (r *userRepo) UpdateSelfUser(ctx context.Context, user *biz.User) error {
|
||||
return r.data.DB().WithContext(ctx).Model(&userPO{}).Where("id = ?", user.ID).Updates(updates).Error
|
||||
}
|
||||
|
||||
func (r *userRepo) FillDepartmentNamePaths(ctx context.Context, user *biz.User) error {
|
||||
func (r *userRepo) FillDepartmentNamePaths(ctx context.Context, user *system.User) error {
|
||||
if user == nil || len(user.Departments) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -463,7 +463,7 @@ func (r *userRepo) FillDepartmentNamePaths(ctx context.Context, user *biz.User)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *userRepo) updateUser(tx *gorm.DB, user *biz.User) error {
|
||||
func (r *userRepo) updateUser(tx *gorm.DB, user *system.User) error {
|
||||
updates := map[string]any{"nick_name": user.NickName, "header_img": user.HeaderImg, "phone": user.Phone, "email": user.Email, "enable": user.Enable}
|
||||
if user.AuthorityID != 0 {
|
||||
updates["authority_id"] = user.AuthorityID
|
||||
@@ -471,7 +471,7 @@ func (r *userRepo) updateUser(tx *gorm.DB, user *biz.User) error {
|
||||
return tx.Model(&userPO{}).Where("id = ?", user.ID).Updates(updates).Error
|
||||
}
|
||||
|
||||
func (r *userRepo) UpdateUserWithAuthorities(ctx context.Context, user *biz.User, authorityIDs []uint) error {
|
||||
func (r *userRepo) UpdateUserWithAuthorities(ctx context.Context, user *system.User, authorityIDs []uint) error {
|
||||
access := &authorityAccessRepo{data: r.data}
|
||||
if err := r.checkUserUpdateAuth(ctx, user); err != nil {
|
||||
return err
|
||||
@@ -520,12 +520,12 @@ func (r *userRepo) UpdatePassword(ctx context.Context, id uint, password string,
|
||||
}
|
||||
return r.data.DB().WithContext(ctx).Model(&userPO{}).Where("id = ?", id).Updates(updates).Error
|
||||
}
|
||||
func (r *userRepo) ListAuthorities(ctx context.Context) ([]*biz.Authority, error) {
|
||||
func (r *userRepo) ListAuthorities(ctx context.Context) ([]*system.Authority, error) {
|
||||
var pos []authorityPO
|
||||
if err := r.data.DB().WithContext(ctx).Order("authority_id").Find(&pos).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*biz.Authority, 0, len(pos))
|
||||
result := make([]*system.Authority, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
a := toBizAuthority(po)
|
||||
result = append(result, &a)
|
||||
|
||||
@@ -2,9 +2,8 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz/system"
|
||||
"testing"
|
||||
|
||||
"kra/internal/biz"
|
||||
)
|
||||
|
||||
func seedStrictUserTree(t *testing.T, data *Data) (actorID, childID, siblingID uint, users []userPO) {
|
||||
@@ -35,10 +34,10 @@ func seedStrictUserTree(t *testing.T, data *Data) (actorID, childID, siblingID u
|
||||
func TestStrictUserMutationsRejectOutsideTargetAndAllowSelfPassword(t *testing.T) {
|
||||
data := newTransactionTestData(t)
|
||||
actorID, _, _, users := seedStrictUserTree(t, data)
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{UserID: users[0].ID, AuthorityID: actorID})
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{UserID: users[0].ID, AuthorityID: actorID})
|
||||
repo := &userRepo{data: data}
|
||||
|
||||
if err := repo.UpdateUser(ctx, &biz.User{ID: users[2].ID, NickName: "changed"}); err == nil {
|
||||
if err := repo.UpdateUser(ctx, &system.User{ID: users[2].ID, NickName: "changed"}); err == nil {
|
||||
t.Fatal("UpdateUser() accepted an out-of-scope target")
|
||||
}
|
||||
if err := repo.DeleteUser(ctx, users[2].ID); err == nil {
|
||||
@@ -55,13 +54,13 @@ func TestStrictUserMutationsRejectOutsideTargetAndAllowSelfPassword(t *testing.T
|
||||
func TestStrictUserCreationAndRoleAssignmentRequireManagedAuthorities(t *testing.T) {
|
||||
data := newTransactionTestData(t)
|
||||
actorID, childID, siblingID, users := seedStrictUserTree(t, data)
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{UserID: users[0].ID, AuthorityID: actorID})
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{UserID: users[0].ID, AuthorityID: actorID})
|
||||
repo := &userRepo{data: data}
|
||||
|
||||
if _, err := repo.CreateUserWithAuthorities(ctx, &biz.User{Username: "outside-create", Password: "hash", AuthorityID: siblingID, Enable: 1}, nil); err == nil {
|
||||
if _, err := repo.CreateUserWithAuthorities(ctx, &system.User{Username: "outside-create", Password: "hash", AuthorityID: siblingID, Enable: 1}, nil); err == nil {
|
||||
t.Fatal("CreateUserWithAuthorities() accepted an out-of-scope primary authority")
|
||||
}
|
||||
if _, err := repo.CreateUserWithAuthorities(ctx, &biz.User{Username: "managed-create", Password: "hash", AuthorityID: childID, Enable: 1}, []uint{childID}); err != nil {
|
||||
if _, err := repo.CreateUserWithAuthorities(ctx, &system.User{Username: "managed-create", Password: "hash", AuthorityID: childID, Enable: 1}, []uint{childID}); err != nil {
|
||||
t.Fatalf("CreateUserWithAuthorities() rejected managed authorities: %v", err)
|
||||
}
|
||||
if err := repo.SetUserAuthorities(ctx, users[1].ID, []uint{siblingID}); err == nil {
|
||||
@@ -75,13 +74,13 @@ func TestStrictUserCreationAndRoleAssignmentRequireManagedAuthorities(t *testing
|
||||
func TestStrictUserUpdateRejectsPrimaryAuthorityOutsideManagedTree(t *testing.T) {
|
||||
data := newTransactionTestData(t)
|
||||
actorID, childID, siblingID, users := seedStrictUserTree(t, data)
|
||||
ctx := biz.NewActorContext(context.Background(), biz.Actor{UserID: users[0].ID, AuthorityID: actorID})
|
||||
ctx := system.NewActorContext(context.Background(), system.Actor{UserID: users[0].ID, AuthorityID: actorID})
|
||||
repo := &userRepo{data: data}
|
||||
|
||||
if err := repo.UpdateUser(ctx, &biz.User{ID: users[1].ID, NickName: "changed", AuthorityID: siblingID}); err == nil {
|
||||
if err := repo.UpdateUser(ctx, &system.User{ID: users[1].ID, NickName: "changed", AuthorityID: siblingID}); err == nil {
|
||||
t.Fatal("UpdateUser() accepted a primary authority outside the managed tree")
|
||||
}
|
||||
if err := repo.UpdateUserWithAuthorities(ctx, &biz.User{ID: users[1].ID, NickName: "changed", AuthorityID: siblingID}, []uint{childID}); err == nil {
|
||||
if err := repo.UpdateUserWithAuthorities(ctx, &system.User{ID: users[1].ID, NickName: "changed", AuthorityID: siblingID}, []uint{childID}); err == nil {
|
||||
t.Fatal("UpdateUserWithAuthorities() accepted a primary authority outside the managed tree")
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,13 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz/system"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"kra/internal/biz"
|
||||
"kra/pkg/database/pagination"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type versionPO struct {
|
||||
@@ -25,11 +26,11 @@ func (versionPO) TableName() string { return "sys_versions" }
|
||||
|
||||
type versionRepo struct{ data Provider }
|
||||
|
||||
func NewVersionRepo(data Provider) biz.VersionRepo { return &versionRepo{data: data} }
|
||||
func versionFromPO(v versionPO) *biz.Version {
|
||||
return &biz.Version{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, Name: v.Name, Code: v.Code, Description: v.Description, Data: v.Data}
|
||||
func NewVersionRepo(data Provider) system.VersionRepo { return &versionRepo{data: data} }
|
||||
func versionFromPO(v versionPO) *system.Version {
|
||||
return &system.Version{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, Name: v.Name, Code: v.Code, Description: v.Description, Data: v.Data}
|
||||
}
|
||||
func (r *versionRepo) CreateVersion(ctx context.Context, v *biz.Version) error {
|
||||
func (r *versionRepo) CreateVersion(ctx context.Context, v *system.Version) error {
|
||||
po := versionPO{Name: v.Name, Code: v.Code, Description: v.Description, Data: v.Data}
|
||||
if err := r.data.DB().WithContext(ctx).Create(&po).Error; err != nil {
|
||||
return err
|
||||
@@ -40,14 +41,14 @@ func (r *versionRepo) CreateVersion(ctx context.Context, v *biz.Version) error {
|
||||
func (r *versionRepo) DeleteVersions(ctx context.Context, ids []uint) error {
|
||||
return r.data.DB().WithContext(ctx).Where("id IN ?", ids).Delete(&versionPO{}).Error
|
||||
}
|
||||
func (r *versionRepo) FindVersion(ctx context.Context, id uint) (*biz.Version, error) {
|
||||
func (r *versionRepo) FindVersion(ctx context.Context, id uint) (*system.Version, error) {
|
||||
var po versionPO
|
||||
if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return versionFromPO(po), nil
|
||||
}
|
||||
func (r *versionRepo) ListVersions(ctx context.Context, page, size int, name, code string, createdAtRange []*time.Time) ([]*biz.Version, int64, error) {
|
||||
func (r *versionRepo) ListVersions(ctx context.Context, page, size int, name, code string, createdAtRange []*time.Time) ([]*system.Version, int64, error) {
|
||||
db := r.data.DB().WithContext(ctx).Model(&versionPO{})
|
||||
if len(createdAtRange) == 2 && createdAtRange[0] != nil && createdAtRange[1] != nil {
|
||||
db = db.Where("created_at BETWEEN ? AND ?", createdAtRange[0], createdAtRange[1])
|
||||
@@ -66,21 +67,21 @@ func (r *versionRepo) ListVersions(ctx context.Context, page, size int, name, co
|
||||
if err := pagination.Apply(db, page, size, 100).Find(&pos).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]*biz.Version, 0, len(pos))
|
||||
out := make([]*system.Version, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
out = append(out, versionFromPO(po))
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func (r *versionRepo) BuildVersionBundle(ctx context.Context, menuIDs, apiIDs, dictIDs []uint) (*biz.VersionBundle, error) {
|
||||
bundle := &biz.VersionBundle{}
|
||||
func (r *versionRepo) BuildVersionBundle(ctx context.Context, menuIDs, apiIDs, dictIDs []uint) (*system.VersionBundle, error) {
|
||||
bundle := &system.VersionBundle{}
|
||||
var menus []menuPO
|
||||
if len(menuIDs) > 0 {
|
||||
if err := r.data.DB().WithContext(ctx).Where("id IN ?", menuIDs).Order("sort,id").Find(&menus).Error; err != nil {
|
||||
return nil, &biz.VersionStageError{Stage: biz.VersionStageMenus, Err: err}
|
||||
return nil, &system.VersionStageError{Stage: system.VersionStageMenus, Err: err}
|
||||
}
|
||||
}
|
||||
byID := map[uint]*biz.Menu{}
|
||||
byID := map[uint]*system.Menu{}
|
||||
for _, po := range menus {
|
||||
v := menuFromPO(po)
|
||||
byID[po.ID] = v
|
||||
@@ -88,20 +89,20 @@ func (r *versionRepo) BuildVersionBundle(ctx context.Context, menuIDs, apiIDs, d
|
||||
if len(menuIDs) > 0 {
|
||||
var buttons []menuButtonPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("sys_base_menu_id IN ?", menuIDs).Find(&buttons).Error; err != nil {
|
||||
return nil, &biz.VersionStageError{Stage: biz.VersionStageMenus, Err: err}
|
||||
return nil, &system.VersionStageError{Stage: system.VersionStageMenus, Err: err}
|
||||
}
|
||||
for _, button := range buttons {
|
||||
if menu := byID[button.MenuID]; menu != nil {
|
||||
menu.Buttons = append(menu.Buttons, &biz.MenuButton{Name: button.Name, Description: button.Description})
|
||||
menu.Buttons = append(menu.Buttons, &system.MenuButton{Name: button.Name, Description: button.Description})
|
||||
}
|
||||
}
|
||||
var parameters []menuParameterPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("sys_base_menu_id IN ?", menuIDs).Find(¶meters).Error; err != nil {
|
||||
return nil, &biz.VersionStageError{Stage: biz.VersionStageMenus, Err: err}
|
||||
return nil, &system.VersionStageError{Stage: system.VersionStageMenus, Err: err}
|
||||
}
|
||||
for _, parameter := range parameters {
|
||||
if menu := byID[parameter.MenuID]; menu != nil {
|
||||
menu.Parameters = append(menu.Parameters, &biz.MenuParameter{Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
|
||||
menu.Parameters = append(menu.Parameters, &system.MenuParameter{Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,16 +114,16 @@ func (r *versionRepo) BuildVersionBundle(ctx context.Context, menuIDs, apiIDs, d
|
||||
bundle.Menus = append(bundle.Menus, v)
|
||||
}
|
||||
}
|
||||
var sortMenus func([]*biz.Menu)
|
||||
sortMenus = func(items []*biz.Menu) {
|
||||
var sortMenus func([]*system.Menu)
|
||||
sortMenus = func(items []*system.Menu) {
|
||||
sort.Slice(items, func(i, j int) bool { return items[i].Sort < items[j].Sort })
|
||||
for _, item := range items {
|
||||
sortMenus(item.Children)
|
||||
}
|
||||
}
|
||||
sortMenus(bundle.Menus)
|
||||
var cleanMenu func(*biz.Menu)
|
||||
cleanMenu = func(menu *biz.Menu) {
|
||||
var cleanMenu func(*system.Menu)
|
||||
cleanMenu = func(menu *system.Menu) {
|
||||
menu.ID = 0
|
||||
menu.ParentID = 0
|
||||
for _, button := range menu.Buttons {
|
||||
@@ -144,7 +145,7 @@ func (r *versionRepo) BuildVersionBundle(ctx context.Context, menuIDs, apiIDs, d
|
||||
var apis []apiPO
|
||||
if len(apiIDs) > 0 {
|
||||
if err := r.data.DB().WithContext(ctx).Where("id IN ?", apiIDs).Find(&apis).Error; err != nil {
|
||||
return nil, &biz.VersionStageError{Stage: biz.VersionStageAPIs, Err: err}
|
||||
return nil, &system.VersionStageError{Stage: system.VersionStageAPIs, Err: err}
|
||||
}
|
||||
}
|
||||
for _, po := range apis {
|
||||
@@ -155,16 +156,16 @@ func (r *versionRepo) BuildVersionBundle(ctx context.Context, menuIDs, apiIDs, d
|
||||
var dictionaries []dictionaryPO
|
||||
if len(dictIDs) > 0 {
|
||||
if err := r.data.DB().WithContext(ctx).Where("id IN ?", dictIDs).Find(&dictionaries).Error; err != nil {
|
||||
return nil, &biz.VersionStageError{Stage: biz.VersionStageDictionaries, Err: err}
|
||||
return nil, &system.VersionStageError{Stage: system.VersionStageDictionaries, Err: err}
|
||||
}
|
||||
}
|
||||
dictionariesRepo := &dictionaryRepo{data: r.data}
|
||||
for _, po := range dictionaries {
|
||||
v := dictionaryFromPO(po)
|
||||
v.ID = 0
|
||||
details, _, err := dictionariesRepo.ListDictionaryDetails(ctx, 0, 0, biz.DictionaryDetailFilter{DictionaryID: po.ID})
|
||||
details, _, err := dictionariesRepo.ListDictionaryDetails(ctx, 0, 0, system.DictionaryDetailFilter{DictionaryID: po.ID})
|
||||
if err != nil {
|
||||
return nil, &biz.VersionStageError{Stage: biz.VersionStageDictionaries, Err: err}
|
||||
return nil, &system.VersionStageError{Stage: system.VersionStageDictionaries, Err: err}
|
||||
}
|
||||
for _, d := range details {
|
||||
d.ID = 0
|
||||
@@ -178,27 +179,27 @@ func (r *versionRepo) BuildVersionBundle(ctx context.Context, menuIDs, apiIDs, d
|
||||
}
|
||||
return bundle, nil
|
||||
}
|
||||
func (r *versionRepo) ImportVersionBundle(ctx context.Context, b *biz.VersionBundle) error {
|
||||
func (r *versionRepo) ImportVersionBundle(ctx context.Context, b *system.VersionBundle) error {
|
||||
db := r.data.DB().WithContext(ctx)
|
||||
if len(b.Menus) > 0 {
|
||||
if err := db.Transaction(func(tx *gorm.DB) error { return importVersionMenus(tx, b.Menus, 0) }); err != nil {
|
||||
return &biz.VersionStageError{Stage: biz.VersionStageMenus, Err: err}
|
||||
return &system.VersionStageError{Stage: system.VersionStageMenus, Err: err}
|
||||
}
|
||||
}
|
||||
if len(b.APIs) > 0 {
|
||||
if err := db.Transaction(func(tx *gorm.DB) error { return importVersionAPIs(tx, b.APIs) }); err != nil {
|
||||
return &biz.VersionStageError{Stage: biz.VersionStageAPIs, Err: err}
|
||||
return &system.VersionStageError{Stage: system.VersionStageAPIs, Err: err}
|
||||
}
|
||||
}
|
||||
if len(b.Dictionaries) > 0 {
|
||||
if err := db.Transaction(func(tx *gorm.DB) error { return importVersionDictionaries(tx, b.Dictionaries) }); err != nil {
|
||||
return &biz.VersionStageError{Stage: biz.VersionStageDictionaries, Err: err}
|
||||
return &system.VersionStageError{Stage: system.VersionStageDictionaries, Err: err}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func importVersionMenus(tx *gorm.DB, items []*biz.Menu, parentID uint) error {
|
||||
func importVersionMenus(tx *gorm.DB, items []*system.Menu, parentID uint) error {
|
||||
for _, item := range items {
|
||||
var existing menuPO
|
||||
if err := tx.Where("name = ? AND path = ?", item.Name, item.Path).First(&existing).Error; err == nil {
|
||||
@@ -230,7 +231,7 @@ func importVersionMenus(tx *gorm.DB, items []*biz.Menu, parentID uint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func importVersionAPIs(tx *gorm.DB, items []*biz.API) error {
|
||||
func importVersionAPIs(tx *gorm.DB, items []*system.API) error {
|
||||
for _, item := range items {
|
||||
var existing apiPO
|
||||
if err := tx.Where("path = ? AND method = ?", item.Path, item.Method).First(&existing).Error; err == nil {
|
||||
@@ -243,7 +244,7 @@ func importVersionAPIs(tx *gorm.DB, items []*biz.API) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func importVersionDictionaries(tx *gorm.DB, items []*biz.Dictionary) error {
|
||||
func importVersionDictionaries(tx *gorm.DB, items []*system.Dictionary) error {
|
||||
for _, item := range items {
|
||||
var existing dictionaryPO
|
||||
if err := tx.Where("type = ?", item.Type).First(&existing).Error; err == nil {
|
||||
|
||||
Reference in New Issue
Block a user