This commit is contained in:
yvan 2026-08-15 03:14:56 +08:00
parent a34133cce5
commit 2a3c55bb53
71 changed files with 1445 additions and 1076 deletions

View File

@ -13,6 +13,7 @@ import (
"kra/internal/data"
"kra/internal/server"
"kra/internal/service"
"kra/internal/worker"
"github.com/go-kratos/kratos/v3"
"github.com/google/wire"
@ -20,5 +21,5 @@ import (
// wireApp init kratos application.
func wireApp(*conf.Server, *conf.Runtime, *slog.Logger) (*kratos.App, func(), error) {
panic(wire.Build(server.ProviderSet, data.ProviderSet, biz.ProviderSet, service.ProviderSet, newApp))
panic(wire.Build(server.ProviderSet, worker.ProviderSet, data.ProviderSet, biz.ProviderSet, service.ProviderSet, newApp))
}

View File

@ -115,7 +115,7 @@ func wireApp(confServer *conf.Server, runtime *conf.Runtime, logger *slog.Logger
apiToken := handler.NewAPIToken(tokenService)
initializationRepo := data.NewInitializationRepo(dataData)
systemConfigUsecase := biz.NewSystemConfigUsecase(initializationRepo, taskRuntime)
systemConfigService := service.NewSystemConfigService(systemConfigUsecase, runtime)
systemConfigService := service.NewSystemConfigService(systemConfigUsecase, runtimeSettings)
securityRepo := data.NewSecurityRepo(dataData)
securityUsecase := biz.NewSecurityUsecase(securityRepo, cache, runtimeSettings, tokenUsecase)
securityService := service.NewSecurityService(securityUsecase)
@ -130,7 +130,7 @@ func wireApp(confServer *conf.Server, runtime *conf.Runtime, logger *slog.Logger
navigation := handler.NewNavigation(userService)
session := handler.NewSession(tokenService)
set := handler.NewSet(authority, menu, api, permission, organization, announcement, email, task, media, audit, export, version, dictionary, parameter, apiToken, systemConfig, public, user, navigation, session)
engine := server.NewGinEngine(runtime, accessControlService, set, securityService, tokenService, auditRecorder, logger)
engine := server.NewGinEngine(runtime, accessControlService, set, authService, securityService, auditRecorder, logger)
httpServer := server.NewGinServer(confServer, engine)
app := newApp(logger, httpServer, taskScheduler)
return app, func() {

View File

@ -1,160 +0,0 @@
package biz
import (
"context"
"strings"
"time"
)
type API struct {
ID uint
CreatedAt time.Time
UpdatedAt time.Time
Path string
Description string
APIGroup string
Method string
OrderKey string
Desc bool
StrictAll bool
}
type APIRepo interface {
CreateAPI(context.Context, *API) error
UpdateAPI(context.Context, *API) error
DeleteAPIs(context.Context, []uint) error
FindAPI(context.Context, uint) (*API, error)
ListAPIs(context.Context, int, int, *API) ([]*API, int64, error)
APIRoleIDs(context.Context, string, string) ([]uint, error)
SetAPIRoles(context.Context, string, string, []uint) error
Authorize(context.Context, uint, string, string) (bool, error)
PolicyPaths(context.Context, uint) ([]*API, error)
SetPolicyPaths(context.Context, uint, []*API) error
IgnoredAPIs(context.Context) ([]*API, error)
SetAPIIgnored(context.Context, string, string, bool) error
ApplyAPISync(context.Context, []*API, []*API) error
}
type AuthorityAccessRepo interface {
CreateAuthority(context.Context, *Authority) error
CopyAuthority(context.Context, uint, *Authority) error
UpdateAuthority(context.Context, *Authority) error
DeleteAuthority(context.Context, uint) error
ListAuthorities(context.Context) ([]*Authority, error)
SetAuthorityUsers(context.Context, uint, []uint) error
AuthorityUserIDs(context.Context, uint) ([]uint, error)
SetDataScope(context.Context, uint, int, []uint) error
DataScopeDepartmentIDs(context.Context, uint) ([]uint, error)
ResolveDataScope(context.Context, uint, uint) (DataScope, error)
}
type PermissionRepo interface {
Buttons(context.Context, uint) ([]*MenuButton, error)
SetAuthorityButtons(context.Context, uint, map[uint][]uint) error
AuthorityButtonIDs(context.Context, uint) ([]uint, error)
SelectedButtons(context.Context, uint, uint) ([]uint, error)
SetSelectedButtons(context.Context, uint, uint, []uint) error
CanRemoveButton(context.Context, uint) (bool, error)
}
type AuthorityUsecase struct{ AuthorityAccessRepo }
func NewAuthorityUsecase(repo AuthorityAccessRepo) *AuthorityUsecase {
return &AuthorityUsecase{AuthorityAccessRepo: repo}
}
type APIUsecase struct{ APIRepo }
func NewAPIUsecase(repo APIRepo) *APIUsecase { return &APIUsecase{APIRepo: repo} }
type PermissionUsecase struct{ PermissionRepo }
func NewPermissionUsecase(repo PermissionRepo) *PermissionUsecase {
return &PermissionUsecase{PermissionRepo: repo}
}
type AccessControlUsecase struct {
authorities AuthorityAccessRepo
apis APIRepo
}
func NewAccessControlUsecase(authorities AuthorityAccessRepo, apis APIRepo) *AccessControlUsecase {
return &AccessControlUsecase{authorities: authorities, apis: apis}
}
func (uc *AccessControlUsecase) Authorize(ctx context.Context, authorityID uint, path, method string) (bool, error) {
if authorityID == 888 {
return true, nil
}
return uc.apis.Authorize(ctx, authorityID, path, method)
}
func (uc *AccessControlUsecase) ResolveDataScope(ctx context.Context, authorityID, userID uint) (DataScope, error) {
return uc.authorities.ResolveDataScope(ctx, authorityID, userID)
}
type APISyncDiff struct {
Added []*API
Deleted []*API
Ignored []*API
}
func (uc *APIUsecase) SyncAPIs(ctx context.Context, routes []*API) (*APISyncDiff, error) {
stored, _, err := uc.ListAPIs(ctx, 0, 0, nil)
if err != nil {
return nil, err
}
ignored, err := uc.IgnoredAPIs(ctx)
if err != nil {
return nil, err
}
key := func(value *API) string { return strings.ToUpper(value.Method) + " " + value.Path }
ignoreSet := make(map[string]bool, len(ignored))
for _, item := range ignored {
ignoreSet[key(item)] = true
}
routeSet := make(map[string]*API, len(routes))
for _, item := range routes {
if !ignoreSet[key(item)] {
routeSet[key(item)] = item
}
}
storedSet := make(map[string]*API, len(stored))
for _, item := range stored {
storedSet[key(item)] = item
}
diff := &APISyncDiff{Ignored: ignored}
for routeKey, item := range routeSet {
if storedSet[routeKey] == nil {
diff.Added = append(diff.Added, item)
}
}
for storedKey, item := range storedSet {
if routeSet[storedKey] == nil && !ignoreSet[storedKey] {
diff.Deleted = append(diff.Deleted, item)
}
}
return diff, nil
}
func (uc *AuthorityUsecase) AuthorityTree(ctx context.Context) ([]*Authority, error) {
items, err := uc.ListAuthorities(ctx)
if err != nil {
return nil, err
}
byID := make(map[uint]*Authority, len(items))
for _, item := range items {
item.Children = nil
byID[item.AuthorityID] = item
}
roots := make([]*Authority, 0)
for _, item := range items {
if item.ParentID != nil && *item.ParentID != 0 && byID[*item.ParentID] != nil {
parent := byID[*item.ParentID]
parent.Children = append(parent.Children, item)
} else {
roots = append(roots, item)
}
}
return roots, nil
}

View File

@ -0,0 +1,23 @@
package biz
import "context"
type AccessControlUsecase struct {
authorities AuthorityAccessRepo
apis APIRepo
}
func NewAccessControlUsecase(authorities AuthorityAccessRepo, apis APIRepo) *AccessControlUsecase {
return &AccessControlUsecase{authorities: authorities, apis: apis}
}
func (uc *AccessControlUsecase) Authorize(ctx context.Context, authorityID uint, path, method string) (bool, error) {
if authorityID == 888 {
return true, nil
}
return uc.apis.Authorize(ctx, authorityID, path, method)
}
func (uc *AccessControlUsecase) ResolveDataScope(ctx context.Context, authorityID, userID uint) (DataScope, error) {
return uc.authorities.ResolveDataScope(ctx, authorityID, userID)
}

84
internal/biz/api.go Normal file
View File

@ -0,0 +1,84 @@
package biz
import (
"context"
"strings"
"time"
)
type API struct {
ID uint
CreatedAt time.Time
UpdatedAt time.Time
Path string
Description string
APIGroup string
Method string
OrderKey string
Desc bool
StrictAll bool
}
type APIRepo interface {
CreateAPI(context.Context, *API) error
UpdateAPI(context.Context, *API) error
DeleteAPIs(context.Context, []uint) error
FindAPI(context.Context, uint) (*API, error)
ListAPIs(context.Context, int, int, *API) ([]*API, int64, error)
APIRoleIDs(context.Context, string, string) ([]uint, error)
SetAPIRoles(context.Context, string, string, []uint) error
Authorize(context.Context, uint, string, string) (bool, error)
PolicyPaths(context.Context, uint) ([]*API, error)
SetPolicyPaths(context.Context, uint, []*API) error
IgnoredAPIs(context.Context) ([]*API, error)
SetAPIIgnored(context.Context, string, string, bool) error
ApplyAPISync(context.Context, []*API, []*API) error
}
type APIUsecase struct{ APIRepo }
func NewAPIUsecase(repo APIRepo) *APIUsecase { return &APIUsecase{APIRepo: repo} }
type APISyncDiff struct {
Added []*API
Deleted []*API
Ignored []*API
}
func (uc *APIUsecase) SyncAPIs(ctx context.Context, routes []*API) (*APISyncDiff, error) {
stored, _, err := uc.ListAPIs(ctx, 0, 0, nil)
if err != nil {
return nil, err
}
ignored, err := uc.IgnoredAPIs(ctx)
if err != nil {
return nil, err
}
key := func(value *API) string { return strings.ToUpper(value.Method) + " " + value.Path }
ignoreSet := make(map[string]bool, len(ignored))
for _, item := range ignored {
ignoreSet[key(item)] = true
}
routeSet := make(map[string]*API, len(routes))
for _, item := range routes {
if !ignoreSet[key(item)] {
routeSet[key(item)] = item
}
}
storedSet := make(map[string]*API, len(stored))
for _, item := range stored {
storedSet[key(item)] = item
}
diff := &APISyncDiff{Ignored: ignored}
for routeKey, item := range routeSet {
if storedSet[routeKey] == nil {
diff.Added = append(diff.Added, item)
}
}
for storedKey, item := range storedSet {
if routeSet[storedKey] == nil && !ignoreSet[storedKey] {
diff.Deleted = append(diff.Deleted, item)
}
}
return diff, nil
}

View File

@ -27,13 +27,6 @@ type OperationRecord struct {
User *User
}
type OperationLogRepo interface {
RecordOperation(context.Context, *OperationRecord) error
ListOperations(context.Context, int, int, *OperationRecord) ([]*OperationRecord, int64, error)
FindOperation(context.Context, uint) (*OperationRecord, error)
DeleteOperations(context.Context, []uint) error
}
type LoginLog struct {
ID uint
CreatedAt time.Time
@ -45,13 +38,6 @@ type LoginLog struct {
FilterByStatus bool
}
type LoginLogRepo interface {
RecordLogin(context.Context, *LoginLog) error
ListLogins(context.Context, int, int, *LoginLog) ([]*LoginLog, int64, error)
FindLogin(context.Context, uint) (*LoginLog, error)
DeleteLogins(context.Context, []uint) error
}
type DataAccessLog struct {
ID uint
CreatedAt time.Time
@ -61,12 +47,6 @@ type DataAccessLog struct {
RequestID, Method, Path, Detail string
}
type DataAccessLogRepo interface {
RecordDataAccess(context.Context, *DataAccessLog) error
ListDataAccess(context.Context, int, int, *DataAccessLog) ([]*DataAccessLog, int64, error)
DeleteDataAccess(context.Context, []uint) error
}
type LogDate struct {
Date string
FileCount int
@ -104,14 +84,6 @@ type ErrorRecord struct {
CreatedAtRange []time.Time
}
type ErrorRecordRepo interface {
CreateError(context.Context, *ErrorRecord) error
UpdateError(context.Context, *ErrorRecord) error
DeleteErrors(context.Context, []uint) error
FindError(context.Context, uint) (*ErrorRecord, error)
ListErrors(context.Context, int, int, *ErrorRecord) ([]*ErrorRecord, int64, error)
}
type AuditRecordRepo interface {
RecordOperation(context.Context, *OperationRecord) error
RecordLogin(context.Context, *LoginLog) error

View File

@ -40,6 +40,11 @@ type AuthenticationResult struct {
NeedChangePassword bool
}
type TokenAuthentication struct {
Claims *AuthClaims
Refreshed *IssuedToken
}
type AuthenticationUsecase struct {
users *UserUsecase
security *SecurityUsecase
@ -148,3 +153,28 @@ func (uc *AuthenticationUsecase) SwitchAuthority(ctx context.Context, userID, au
}
return &AuthenticationResult{User: user, Token: issued.Value, ExpiresAt: issued.ExpiresAt, NeedChangePassword: user.MustChangePassword}, nil
}
func (uc *AuthenticationUsecase) AuthenticateToken(ctx context.Context, token string) (*TokenAuthentication, error) {
claims, err := uc.issuer.ParseToken(token)
if err != nil {
return nil, err
}
disabled, err := uc.security.tokens.IsTokenDisabled(ctx, token)
if err != nil || disabled {
return nil, ErrTokenDisabled
}
result := &TokenAuthentication{Claims: claims}
if claims.BufferTime <= 0 || time.Until(claims.ExpiresAt) >= claims.BufferTime {
return result, nil
}
user := &User{ID: claims.ID, UUID: claims.UUID, Username: claims.Username, NickName: claims.NickName, AuthorityID: claims.AuthorityID, MustChangePassword: claims.MustChangePwd}
issued, err := uc.issuer.IssueToken(user, claims.AuthorityID, claims.MustChangePwd, 0)
if err != nil {
return result, nil
}
if err = uc.security.RotateActiveToken(ctx, claims.Username, token, issued.Value, issued.TTL); err != nil {
return result, nil
}
result.Refreshed = issued
return result, nil
}

44
internal/biz/authority.go Normal file
View File

@ -0,0 +1,44 @@
package biz
import "context"
type AuthorityAccessRepo interface {
CreateAuthority(context.Context, *Authority) error
CopyAuthority(context.Context, uint, *Authority) error
UpdateAuthority(context.Context, *Authority) error
DeleteAuthority(context.Context, uint) error
ListAuthorities(context.Context) ([]*Authority, error)
SetAuthorityUsers(context.Context, uint, []uint) error
AuthorityUserIDs(context.Context, uint) ([]uint, error)
SetDataScope(context.Context, uint, int, []uint) error
DataScopeDepartmentIDs(context.Context, uint) ([]uint, error)
ResolveDataScope(context.Context, uint, uint) (DataScope, error)
}
type AuthorityUsecase struct{ AuthorityAccessRepo }
func NewAuthorityUsecase(repo AuthorityAccessRepo) *AuthorityUsecase {
return &AuthorityUsecase{AuthorityAccessRepo: repo}
}
func (uc *AuthorityUsecase) AuthorityTree(ctx context.Context) ([]*Authority, error) {
items, err := uc.ListAuthorities(ctx)
if err != nil {
return nil, err
}
byID := make(map[uint]*Authority, len(items))
for _, item := range items {
item.Children = nil
byID[item.AuthorityID] = item
}
roots := make([]*Authority, 0)
for _, item := range items {
if item.ParentID != nil && *item.ParentID != 0 && byID[*item.ParentID] != nil {
parent := byID[*item.ParentID]
parent.Children = append(parent.Children, item)
} else {
roots = append(roots, item)
}
}
return roots, nil
}

View File

@ -0,0 +1,46 @@
package biz
import (
"context"
"time"
)
type Department struct {
ID uint
CreatedAt time.Time
UpdatedAt time.Time
Name string
ParentID uint
Ancestors string
Sort int
LeaderID uint
Leader *User
Status bool
Children []*Department
NamePath string
}
type DepartmentRepo interface {
CreateDepartment(context.Context, *Department) error
UpdateDepartment(context.Context, *Department) error
DeleteDepartment(context.Context, uint) error
FindDepartment(context.Context, uint) (*Department, error)
ListDepartments(context.Context, string) ([]*Department, error)
DepartmentUserIDs(context.Context, uint) ([]uint, error)
SetDepartmentUsers(context.Context, uint, []uint) error
SetUserDepartments(context.Context, uint, []uint, uint) error
}
type DepartmentUsecase struct{ DepartmentRepo }
func NewDepartmentUsecase(repo DepartmentRepo) *DepartmentUsecase {
return &DepartmentUsecase{DepartmentRepo: repo}
}
func (uc *DepartmentUsecase) Departments(ctx context.Context, name string) ([]*Department, error) {
return uc.ListDepartments(ctx, name)
}
func (uc *DepartmentUsecase) Department(ctx context.Context, id uint) (*Department, error) {
return uc.FindDepartment(ctx, id)
}

View File

@ -2,6 +2,7 @@ package biz
import (
"context"
"errors"
"io"
"time"
)
@ -68,6 +69,28 @@ type IssuedToken struct {
TTL time.Duration
}
type AuthClaims struct {
UUID string
ID uint
Username string
NickName string
AuthorityID uint
BufferTime time.Duration
MustChangePwd bool
Issuer string
ExpiresAt time.Time
}
var (
ErrTokenExpired = errors.New("token expired")
ErrTokenMalformed = errors.New("token malformed")
ErrTokenSignatureInvalid = errors.New("token signature invalid")
ErrTokenNotValidYet = errors.New("token not valid yet")
ErrTokenInvalid = errors.New("token invalid")
ErrTokenDisabled = errors.New("token disabled")
)
type TokenIssuer interface {
IssueToken(*User, uint, bool, time.Duration) (*IssuedToken, error)
ParseToken(string) (*AuthClaims, error)
}

View File

@ -0,0 +1,18 @@
package biz
import "context"
type PermissionRepo interface {
Buttons(context.Context, uint) ([]*MenuButton, error)
SetAuthorityButtons(context.Context, uint, map[uint][]uint) error
AuthorityButtonIDs(context.Context, uint) ([]uint, error)
SelectedButtons(context.Context, uint, uint) ([]uint, error)
SetSelectedButtons(context.Context, uint, uint, []uint) error
CanRemoveButton(context.Context, uint) (bool, error)
}
type PermissionUsecase struct{ PermissionRepo }
func NewPermissionUsecase(repo PermissionRepo) *PermissionUsecase {
return &PermissionUsecase{PermissionRepo: repo}
}

View File

@ -5,45 +5,6 @@ import (
"time"
)
type Department struct {
ID uint
CreatedAt time.Time
UpdatedAt time.Time
Name string
ParentID uint
Ancestors string
Sort int
LeaderID uint
Leader *User
Status bool
Children []*Department
NamePath string
}
type DepartmentRepo interface {
CreateDepartment(context.Context, *Department) error
UpdateDepartment(context.Context, *Department) error
DeleteDepartment(context.Context, uint) error
FindDepartment(context.Context, uint) (*Department, error)
ListDepartments(context.Context, string) ([]*Department, error)
DepartmentUserIDs(context.Context, uint) ([]uint, error)
SetDepartmentUsers(context.Context, uint, []uint) error
SetUserDepartments(context.Context, uint, []uint, uint) error
}
type DepartmentUsecase struct{ DepartmentRepo }
func NewDepartmentUsecase(repo DepartmentRepo) *DepartmentUsecase {
return &DepartmentUsecase{DepartmentRepo: repo}
}
func (uc *DepartmentUsecase) Departments(ctx context.Context, name string) ([]*Department, error) {
return uc.ListDepartments(ctx, name)
}
func (uc *DepartmentUsecase) Department(ctx context.Context, id uint) (*Department, error) {
return uc.FindDepartment(ctx, id)
}
type Position struct {
ID uint
CreatedAt time.Time
@ -81,6 +42,7 @@ func NewPositionUsecase(repo PositionRepo) *PositionUsecase {
func (uc *PositionUsecase) Positions(ctx context.Context, page, size int, filter *PositionListFilter) ([]*Position, int64, error) {
return uc.ListPositions(ctx, page, size, filter)
}
func (uc *PositionUsecase) Position(ctx context.Context, id uint) (*Position, error) {
return uc.FindPosition(ctx, id)
}

View File

@ -2,6 +2,7 @@ package biz
import (
"context"
"encoding/json"
"errors"
)
@ -24,6 +25,9 @@ type InitializationRepo interface {
PersistAdminConfig(context.Context, []byte) error
PersistRuntimeConfig(context.Context, []byte, []byte) error
ReloadConfig(context.Context) error
ConfigurationJSON() (json.RawMessage, error)
SaveConfigurationJSON(context.Context, json.RawMessage) error
DiskMountPoints() []string
}
type SystemConfigUsecase struct {
@ -68,3 +72,13 @@ func (uc *SystemConfigUsecase) ReloadConfig(ctx context.Context) error {
}
return nil
}
func (uc *SystemConfigUsecase) ConfigurationJSON() (json.RawMessage, error) {
return uc.repo.ConfigurationJSON()
}
func (uc *SystemConfigUsecase) SaveConfigurationJSON(ctx context.Context, value json.RawMessage) error {
return uc.repo.SaveConfigurationJSON(ctx, value)
}
func (uc *SystemConfigUsecase) DiskMountPoints() []string { return uc.repo.DiskMountPoints() }

View File

@ -1,15 +0,0 @@
package data
import "kra/internal/biz"
type authorityAccessRepo struct{ data *Data }
type apiRepo struct{ data *Data }
type permissionRepo struct{ data *Data }
func NewAuthorityAccessRepo(data *Data) biz.AuthorityAccessRepo {
return &authorityAccessRepo{data: data}
}
func NewAPIRepo(data *Data) biz.APIRepo { return &apiRepo{data: data} }
func NewPermissionRepo(data *Data) biz.PermissionRepo { return &permissionRepo{data: data} }

View File

@ -14,6 +14,10 @@ import (
"gorm.io/gorm"
)
type apiRepo struct{ data *Data }
func NewAPIRepo(data *Data) biz.APIRepo { return &apiRepo{data: data} }
type apiPO struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time

View File

@ -10,6 +10,10 @@ import (
"gorm.io/gorm"
)
type apiTokenRepo struct{ data *Data }
func NewAPITokenRepo(data *Data) biz.APITokenRepo { return &apiTokenRepo{data: data} }
type apiTokenPO struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
@ -45,7 +49,7 @@ func (r *apiTokenRepo) UserHasAuthority(ctx context.Context, userID, authorityID
if err != nil {
return nil, false, err
}
user, err := (&userRepo{data: r.data}).toBizUser(ctx, &po)
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 {

View File

@ -11,6 +11,12 @@ import (
"gorm.io/gorm"
)
type authorityAccessRepo struct{ data *Data }
func NewAuthorityAccessRepo(data *Data) biz.AuthorityAccessRepo {
return &authorityAccessRepo{data: data}
}
func (r *authorityAccessRepo) strictAuthorityIDs(ctx context.Context, actorID uint) (map[uint]bool, error) {
allowed := make(map[uint]bool)
if actorID == 0 {

View File

@ -0,0 +1,303 @@
package data
import (
"context"
"encoding/json"
"kra/internal/conf"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/durationpb"
)
type configurationEnvelope struct {
Data json.RawMessage `json:"data"`
Admin json.RawMessage `json:"admin"`
Email *struct {
To string `json:"to"`
From string `json:"from"`
Host string `json:"host"`
Secret string `json:"secret"`
Nickname string `json:"nickname"`
Port int32 `json:"port"`
IsSSL bool `json:"is-ssl"`
IsLoginAuth bool `json:"is-loginauth"`
} `json:"email"`
}
func (r *initializationRepo) ConfigurationJSON() (json.RawMessage, error) {
dataConfig, adminConfig := r.data.runtime.Values()
admin := map[string]any{"routerPrefix": ""}
email := map[string]any{}
if adminConfig != nil {
admin["routerPrefix"] = adminConfig.RouterPrefix
if adminConfig.System != nil {
admin["system"] = map[string]any{"useRedis": adminConfig.System.UseRedis, "useMultipoint": adminConfig.System.UseMultipoint, "useStrictAuth": adminConfig.System.UseStrictAuth, "disableAutoMigrate": adminConfig.System.DisableAutoMigrate, "useMongo": adminConfig.System.UseMongo}
}
if adminConfig.Jwt != nil {
admin["jwt"] = map[string]any{"signingKey": "******", "expiresTime": durationString(adminConfig.Jwt.ExpiresTime), "bufferTime": durationString(adminConfig.Jwt.BufferTime), "issuer": adminConfig.Jwt.Issuer}
}
if adminConfig.Captcha != nil {
admin["captcha"] = map[string]any{"keyLong": adminConfig.Captcha.KeyLong, "imgWidth": adminConfig.Captcha.ImgWidth, "imgHeight": adminConfig.Captcha.ImgHeight, "storeExpiration": durationString(adminConfig.Captcha.StoreExpiration)}
}
if adminConfig.Local != nil {
admin["local"] = map[string]any{"storePath": adminConfig.Local.StorePath, "pathPrefix": adminConfig.Local.PathPrefix}
}
if adminConfig.Media != nil {
admin["media"] = map[string]any{"sessionTtl": adminConfig.Media.SessionTtl, "maxFileSize": adminConfig.Media.MaxFileSize}
}
if adminConfig.Email != nil {
email = map[string]any{"to": adminConfig.Email.To, "from": adminConfig.Email.From, "host": adminConfig.Email.Host, "secret": "******", "nickname": adminConfig.Email.Nickname, "port": adminConfig.Email.Port, "is-ssl": adminConfig.Email.IsSsl, "is-loginauth": adminConfig.Email.IsLoginAuth}
}
if adminConfig.Storage != nil {
storage := proto.Clone(adminConfig.Storage).(*conf.AdminBackend_Storage)
maskStorageSecrets(storage)
admin["storage"] = storage
}
if adminConfig.Zap != nil {
admin["zap"] = adminConfig.Zap
}
if adminConfig.Cors != nil {
admin["cors"] = adminConfig.Cors
}
if adminConfig.App != nil {
admin["app"] = adminConfig.App
}
}
maskDataSecrets(dataConfig)
dataMap := map[string]any{}
if dataConfig != nil {
raw, err := protojson.MarshalOptions{UseProtoNames: true}.Marshal(dataConfig)
if err != nil {
return nil, err
}
if err = json.Unmarshal(raw, &dataMap); err != nil {
return nil, err
}
}
return json.Marshal(map[string]any{"config": map[string]any{"admin": admin, "email": email, "data": dataMap}})
}
func (r *initializationRepo) SaveConfigurationJSON(ctx context.Context, raw json.RawMessage) error {
currentData, currentAdmin := r.data.runtime.Values()
if currentAdmin == nil {
return nil
}
nextData := cloneDataConfig(currentData)
nextAdmin := proto.Clone(currentAdmin).(*conf.AdminBackend)
var value configurationEnvelope
if err := json.Unmarshal(raw, &value); err != nil {
return err
}
options := protojson.UnmarshalOptions{DiscardUnknown: true}
if len(value.Data) > 0 && string(value.Data) != "null" {
patch := &conf.Data{}
if err := options.Unmarshal(value.Data, patch); err != nil {
return err
}
applyDataPatch(nextData, patch)
}
if len(value.Admin) > 0 && string(value.Admin) != "null" {
patch := &conf.AdminBackend{}
if err := options.Unmarshal(value.Admin, patch); err != nil {
return err
}
applyAdminPatch(nextAdmin, patch)
}
preserveDataSecrets(nextData, currentData)
preserveAdminSecrets(nextAdmin, currentAdmin)
if value.Email != nil {
if nextAdmin.Email == nil {
nextAdmin.Email = &conf.AdminBackend_Email{}
}
nextAdmin.Email.To, nextAdmin.Email.From, nextAdmin.Email.Host = value.Email.To, value.Email.From, value.Email.Host
nextAdmin.Email.Nickname, nextAdmin.Email.Port = value.Email.Nickname, value.Email.Port
nextAdmin.Email.IsSsl, nextAdmin.Email.IsLoginAuth = value.Email.IsSSL, value.Email.IsLoginAuth
if value.Email.Secret != "" && value.Email.Secret != "******" {
nextAdmin.Email.Secret = value.Email.Secret
}
}
nextAdmin.ConfigPath = currentAdmin.ConfigPath
dataRaw, err := protojson.MarshalOptions{UseProtoNames: true}.Marshal(nextData)
if err != nil {
return err
}
adminRaw, err := protojson.MarshalOptions{UseProtoNames: true}.Marshal(nextAdmin)
if err != nil {
return err
}
return r.PersistRuntimeConfig(ctx, dataRaw, adminRaw)
}
func (r *initializationRepo) DiskMountPoints() []string {
config := r.data.runtime.Admin()
if config == nil {
return nil
}
points := make([]string, 0, len(config.DiskList))
for _, item := range config.DiskList {
if item != nil && item.MountPoint != "" {
points = append(points, item.MountPoint)
}
}
return points
}
func cloneDataConfig(value *conf.Data) *conf.Data {
if value == nil {
return &conf.Data{}
}
return proto.Clone(value).(*conf.Data)
}
func durationString(value *durationpb.Duration) string {
if value == nil {
return "0s"
}
return value.AsDuration().String()
}
func applyDataPatch(target, patch *conf.Data) {
if patch.Database != nil {
target.Database = patch.Database
}
if patch.Redis != nil {
target.Redis = patch.Redis
}
if patch.Mongo != nil {
target.Mongo = patch.Mongo
}
if patch.DatabaseList != nil {
target.DatabaseList = patch.DatabaseList
}
if patch.RedisList != nil {
target.RedisList = patch.RedisList
}
}
func applyAdminPatch(target, patch *conf.AdminBackend) {
target.RouterPrefix = patch.RouterPrefix
if patch.System != nil {
target.System = patch.System
}
if patch.Jwt != nil {
target.Jwt = patch.Jwt
}
if patch.Captcha != nil {
target.Captcha = patch.Captcha
}
if patch.Local != nil {
target.Local = patch.Local
}
if patch.Media != nil {
target.Media = patch.Media
}
if patch.Storage != nil {
target.Storage = patch.Storage
}
if patch.Zap != nil {
target.Zap = patch.Zap
}
if patch.Cors != nil {
target.Cors = patch.Cors
}
if patch.App != nil {
target.App = patch.App
}
}
func maskDataSecrets(value *conf.Data) {
if value == nil {
return
}
if value.Database != nil {
value.Database.Password = "******"
}
if value.Redis != nil {
value.Redis.Password = "******"
}
if value.Mongo != nil {
value.Mongo.Password = "******"
}
for _, item := range value.DatabaseList {
if item != nil {
item.Password = "******"
}
}
for _, item := range value.RedisList {
if item != nil {
item.Password = "******"
}
}
}
func preserveDataSecrets(next, current *conf.Data) {
if next == nil || current == nil {
return
}
if next.Database != nil && current.Database != nil && maskedSecret(next.Database.Password) {
next.Database.Password = current.Database.Password
}
if next.Redis != nil && current.Redis != nil && maskedSecret(next.Redis.Password) {
next.Redis.Password = current.Redis.Password
}
if next.Mongo != nil && current.Mongo != nil && maskedSecret(next.Mongo.Password) {
next.Mongo.Password = current.Mongo.Password
}
for i, item := range next.DatabaseList {
if item != nil && i < len(current.DatabaseList) && current.DatabaseList[i] != nil && maskedSecret(item.Password) {
item.Password = current.DatabaseList[i].Password
}
}
for i, item := range next.RedisList {
if item != nil && i < len(current.RedisList) && current.RedisList[i] != nil && maskedSecret(item.Password) {
item.Password = current.RedisList[i].Password
}
}
}
func preserveAdminSecrets(next, current *conf.AdminBackend) {
if next.Jwt != nil && current.Jwt != nil && maskedSecret(next.Jwt.SigningKey) {
next.Jwt.SigningKey = current.Jwt.SigningKey
}
if next.Email != nil && current.Email != nil && maskedSecret(next.Email.Secret) {
next.Email.Secret = current.Email.Secret
}
preserveStorageSecrets(next.Storage, current.Storage)
}
func maskedSecret(value string) bool { return value == "" || value == "******" }
func objectStores(storage *conf.AdminBackend_Storage) []*conf.AdminBackend_ObjectStore {
if storage == nil {
return nil
}
return []*conf.AdminBackend_ObjectStore{storage.AliyunOss, storage.HuaweiObs, storage.TencentCos, storage.AwsS3, storage.CloudflareR2, storage.Minio}
}
func maskStorageSecrets(storage *conf.AdminBackend_Storage) {
if storage.Qiniu != nil {
storage.Qiniu.SecretKey = "******"
}
for _, item := range objectStores(storage) {
if item != nil {
item.SecretKey = "******"
}
}
}
func preserveStorageSecrets(next, current *conf.AdminBackend_Storage) {
if next == nil || current == nil {
return
}
if next.Qiniu != nil && current.Qiniu != nil && maskedSecret(next.Qiniu.SecretKey) {
next.Qiniu.SecretKey = current.Qiniu.SecretKey
}
nextItems, currentItems := objectStores(next), objectStores(current)
for i := range nextItems {
if nextItems[i] != nil && currentItems[i] != nil && maskedSecret(nextItems[i].SecretKey) {
nextItems[i].SecretKey = currentItems[i].SecretKey
}
}
}

View File

@ -12,6 +12,10 @@ import (
"gorm.io/gorm"
)
type departmentRepo struct{ data *Data }
func NewDepartmentRepo(data *Data) biz.DepartmentRepo { return &departmentRepo{data: data} }
type departmentPO struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time

View File

@ -12,6 +12,10 @@ import (
"gorm.io/gorm"
)
type dictionaryRepo struct{ data *Data }
func NewDictionaryRepo(data *Data) biz.DictionaryRepo { return &dictionaryRepo{data: data} }
type dictionaryPO struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time

View File

@ -1,10 +0,0 @@
package data
import "kra/internal/biz"
type departmentRepo struct{ data *Data }
type positionRepo struct{ data *Data }
func NewDepartmentRepo(data *Data) biz.DepartmentRepo { return &departmentRepo{data: data} }
func NewPositionRepo(data *Data) biz.PositionRepo { return &positionRepo{data: data} }

View File

@ -10,6 +10,10 @@ import (
"gorm.io/gorm/clause"
)
type parameterRepo struct{ data *Data }
func NewParameterRepo(data *Data) biz.ParameterRepo { return &parameterRepo{data: data} }
type parameterPO struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time

View File

@ -8,6 +8,10 @@ import (
"gorm.io/gorm"
)
type permissionRepo struct{ data *Data }
func NewPermissionRepo(data *Data) biz.PermissionRepo { return &permissionRepo{data: data} }
type menuButtonPO struct {
ID uint `gorm:"primaryKey"`
Name string

View File

@ -10,6 +10,10 @@ import (
"gorm.io/gorm"
)
type positionRepo struct{ data *Data }
func NewPositionRepo(data *Data) biz.PositionRepo { return &positionRepo{data: data} }
type positionPO struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time

View File

@ -1,6 +1,7 @@
package data
import (
"errors"
"time"
"kra/internal/biz"
@ -92,3 +93,22 @@ func (i *tokenIssuer) IssueToken(user *biz.User, authorityID uint, mustChangePas
}
return &biz.IssuedToken{Value: token, ExpiresAt: claims.ExpiresAt.Time, TTL: expires}, nil
}
func (i *tokenIssuer) ParseToken(token string) (*biz.AuthClaims, error) {
claims, err := adminauth.Parse(token, i.settings.JWTSettings().SigningKey)
if err != nil {
switch {
case errors.Is(err, adminauth.ErrTokenExpired):
return nil, biz.ErrTokenExpired
case errors.Is(err, adminauth.ErrTokenMalformed):
return nil, biz.ErrTokenMalformed
case errors.Is(err, adminauth.ErrTokenSignatureInvalid):
return nil, biz.ErrTokenSignatureInvalid
case errors.Is(err, adminauth.ErrTokenNotValidYet):
return nil, biz.ErrTokenNotValidYet
default:
return nil, biz.ErrTokenInvalid
}
}
return &biz.AuthClaims{UUID: claims.UUID, ID: claims.ID, Username: claims.Username, NickName: claims.NickName, AuthorityID: claims.AuthorityID, BufferTime: time.Duration(claims.BufferTime) * time.Second, MustChangePwd: claims.MustChangePwd, Issuer: claims.Issuer, ExpiresAt: claims.ExpiresAt.Time}, nil
}

View File

@ -10,6 +10,10 @@ import (
"gorm.io/gorm"
)
type securityRepo struct{ data *Data }
func NewSecurityRepo(data *Data) biz.SecurityRepo { return &securityRepo{data: data} }
type securityConfigPO struct {
ID uint `gorm:"primaryKey"`
CaptchaOpen int

View File

@ -1,16 +0,0 @@
package data
import "kra/internal/biz"
type dictionaryRepo struct{ data *Data }
type parameterRepo struct{ data *Data }
type apiTokenRepo struct{ data *Data }
type securityRepo struct{ data *Data }
func NewDictionaryRepo(data *Data) biz.DictionaryRepo { return &dictionaryRepo{data: data} }
func NewParameterRepo(data *Data) biz.ParameterRepo { return &parameterRepo{data: data} }
func NewAPITokenRepo(data *Data) biz.APITokenRepo { return &apiTokenRepo{data: data} }
func NewSecurityRepo(data *Data) biz.SecurityRepo { return &securityRepo{data: data} }

View File

@ -22,7 +22,7 @@ func (r *userRepo) FindUserByUsername(ctx context.Context, username string) (*bi
}
return nil, err
}
return r.toBizUser(ctx, &po)
return r.loadUser(ctx, &po)
}
func (r *userRepo) FindUserByID(ctx context.Context, id uint) (*biz.User, error) {
@ -33,10 +33,10 @@ func (r *userRepo) FindUserByID(ctx context.Context, id uint) (*biz.User, error)
}
return nil, err
}
return r.toBizUser(ctx, &po)
return r.loadUser(ctx, &po)
}
func (r *userRepo) toBizUser(ctx context.Context, po *userPO) (*biz.User, error) {
func (r *userRepo) loadUser(ctx context.Context, po *userPO) (*biz.User, error) {
var authority authorityPO
if err := r.data.gormDB.WithContext(ctx).First(&authority, "authority_id = ?", po.AuthorityID).Error; err != nil {
return nil, err
@ -90,7 +90,7 @@ func baseBizUser(po *userPO) *biz.User {
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}
}
func (r *userRepo) toBizUsers(ctx context.Context, pos []userPO) ([]*biz.User, error) {
func (r *userRepo) loadUsers(ctx context.Context, pos []userPO) ([]*biz.User, error) {
if len(pos) == 0 {
return []*biz.User{}, nil
}
@ -275,7 +275,7 @@ func (r *userRepo) ListUsers(ctx context.Context, page, pageSize int, filter *bi
if err := applyPagination(db.Order(order), page, pageSize, 100).Find(&pos).Error; err != nil {
return nil, 0, err
}
users, err := r.toBizUsers(ctx, pos)
users, err := r.loadUsers(ctx, pos)
return users, total, err
}

View File

@ -20,7 +20,7 @@ import (
kratoshttp "github.com/go-kratos/kratos/v3/transport/http"
)
func NewGinEngine(runtime *conf.Runtime, access *service.AccessControlService, handlers *handler.Set, security *service.SecurityService, tokens *service.TokenService, audit *service.AuditRecorder, logger *slog.Logger) *gin.Engine {
func NewGinEngine(runtime *conf.Runtime, access *service.AccessControlService, handlers *handler.Set, auth *service.AuthService, security *service.SecurityService, audit *service.AuditRecorder, logger *slog.Logger) *gin.Engine {
gin.SetMode(gin.ReleaseMode)
engine := gin.New()
engine.Use(servermiddleware.RequestMeta(), servermiddleware.Recovery(audit, logger), servermiddleware.AccessLog(runtime, logger), servermiddleware.ErrorAudit(audit), servermiddleware.SecurityRateLimit(security), servermiddleware.OperationAudit(runtime, audit))
@ -35,7 +35,7 @@ func NewGinEngine(runtime *conf.Runtime, access *service.AccessControlService, h
serverrouter.RegisterPublic(public, engine, handlers.Public)
private := engine.Group(prefix)
private.Use(servermiddleware.Auth(runtime, security, tokens), servermiddleware.MustChangePassword(), servermiddleware.AccessControl(runtime, access, audit))
private.Use(servermiddleware.Auth(auth), servermiddleware.MustChangePassword(), servermiddleware.AccessControl(runtime, access, audit))
serverrouter.RegisterUser(private, handlers.User)
serverrouter.RegisterNavigation(private, handlers.Navigation)
serverrouter.RegisterSession(private, handlers.Session)

View File

@ -92,17 +92,6 @@ func (h *Dictionary) Export(c *gin.Context) {
httpx.Fail(c, "导出失败")
return
}
delete(item, "ID")
delete(item, "CreatedAt")
delete(item, "UpdatedAt")
delete(item, "DeletedAt")
if details, ok := item["sysDictionaryDetails"].([]map[string]any); ok {
for _, detail := range details {
for _, key := range []string{"ID", "CreatedAt", "UpdatedAt", "DeletedAt", "sysDictionaryID", "parentID", "disabled", "children"} {
delete(detail, key)
}
}
}
httpx.Write(c, httpx.CodeSuccess, item, "导出成功")
}
func (h *Dictionary) Import(c *gin.Context) {
@ -258,5 +247,5 @@ func (h *Dictionary) Path(c *gin.Context) {
httpx.Fail(c, "获取失败")
return
}
httpx.Write(c, httpx.CodeSuccess, gin.H{"path": item["path"]}, "获取成功")
httpx.Write(c, httpx.CodeSuccess, gin.H{"path": item.Path}, "获取成功")
}

View File

@ -44,7 +44,12 @@ func (h *SystemConfig) SetSecurity(c *gin.Context) {
}
func (h *SystemConfig) Get(c *gin.Context) {
httpx.Write(c, httpx.CodeSuccess, h.system.SystemConfig(), "获取成功")
value, err := h.system.SystemConfig()
if err != nil {
httpx.Fail(c, "获取失败")
return
}
httpx.Write(c, httpx.CodeSuccess, value, "获取成功")
}
func (h *SystemConfig) Set(c *gin.Context) {

View File

@ -5,12 +5,10 @@ import (
"net/http"
"strconv"
"strings"
"time"
"kra/internal/conf"
"kra/internal/biz"
"kra/internal/server/httpx"
"kra/internal/service"
"kra/pkg/adminauth"
"github.com/gin-gonic/gin"
"golang.org/x/sync/singleflight"
@ -20,12 +18,7 @@ const claimsKey = "admin_claims"
var refreshTokens singleflight.Group
type refreshedToken struct {
token string
expiresAt int64
}
func Auth(runtime *conf.Runtime, security *service.SecurityService, tokens *service.TokenService) gin.HandlerFunc {
func Auth(auth *service.AuthService) gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("x-token")
if token == "" {
@ -35,73 +28,41 @@ func Auth(runtime *conf.Runtime, security *service.SecurityService, tokens *serv
httpx.NoAuth(c, "未登录或非法访问,请登录")
return
}
secret := ""
config := runtime.Admin()
if config != nil && config.Jwt != nil {
secret = config.Jwt.SigningKey
}
claims, err := adminauth.Parse(token, secret)
value, err, _ := refreshTokens.Do(token, func() (any, error) {
return auth.AuthenticateToken(c.Request.Context(), token)
})
if err != nil {
message := "无法处理此token"
switch {
case errors.Is(err, adminauth.ErrTokenExpired):
case errors.Is(err, biz.ErrTokenExpired):
message = "登录已过期,请重新登录"
case errors.Is(err, adminauth.ErrTokenMalformed):
case errors.Is(err, biz.ErrTokenMalformed):
message = "这不是一个token"
case errors.Is(err, adminauth.ErrTokenSignatureInvalid):
case errors.Is(err, biz.ErrTokenSignatureInvalid):
message = "无效签名"
case errors.Is(err, adminauth.ErrTokenNotValidYet):
case errors.Is(err, biz.ErrTokenNotValidYet):
message = "token尚未激活"
case errors.Is(err, biz.ErrTokenDisabled):
message = "您的帐户异地登陆或令牌失效"
}
httpx.SetTokenCookie(c, "", -1)
httpx.NoAuth(c, message)
return
}
if disabled, checkErr := tokens.IsTokenDisabled(c.Request.Context(), token); checkErr != nil || disabled {
httpx.SetTokenCookie(c, "", -1)
httpx.NoAuth(c, "您的帐户异地登陆或令牌失效")
return
authentication := value.(*biz.TokenAuthentication)
if authentication.Refreshed != nil {
c.Header("new-token", authentication.Refreshed.Value)
c.Header("new-expires-at", strconv.FormatInt(authentication.Refreshed.ExpiresAt.Unix(), 10))
httpx.SetTokenCookie(c, authentication.Refreshed.Value, int(authentication.Refreshed.TTL.Seconds()))
}
if claims.ExpiresAt != nil && claims.BufferTime > 0 && time.Until(claims.ExpiresAt.Time) < time.Duration(claims.BufferTime)*time.Second {
expires, buffer := 7*24*time.Hour, time.Duration(claims.BufferTime)*time.Second
issuer := claims.Issuer
config = runtime.Admin()
if config != nil && config.Jwt != nil {
if config.Jwt.ExpiresTime != nil {
expires = config.Jwt.ExpiresTime.AsDuration()
}
if config.Jwt.BufferTime != nil {
buffer = config.Jwt.BufferTime.AsDuration()
}
if config.Jwt.Issuer != "" {
issuer = config.Jwt.Issuer
}
}
value, refreshErr, _ := refreshTokens.Do(token, func() (any, error) {
newToken, newClaims, generateErr := adminauth.Generate(secret, issuer, expires, buffer, claims.ID, claims.AuthorityID, claims.UUID, claims.Username, claims.NickName, claims.MustChangePwd)
if generateErr != nil {
return nil, generateErr
}
if rotateErr := security.RotateActiveToken(c.Request.Context(), claims.Username, token, newToken, expires); rotateErr != nil {
return nil, rotateErr
}
return refreshedToken{token: newToken, expiresAt: newClaims.ExpiresAt.Unix()}, nil
})
if refreshErr == nil {
refreshed := value.(refreshedToken)
c.Header("new-token", refreshed.token)
c.Header("new-expires-at", strconv.FormatInt(refreshed.expiresAt, 10))
httpx.SetTokenCookie(c, refreshed.token, int(expires.Seconds()))
}
}
c.Set(claimsKey, claims)
c.Set(claimsKey, authentication.Claims)
c.Next()
}
}
func Claims(c *gin.Context) *adminauth.Claims {
func Claims(c *gin.Context) *biz.AuthClaims {
value, _ := c.Get(claimsKey)
claims, _ := value.(*adminauth.Claims)
claims, _ := value.(*biz.AuthClaims)
return claims
}

View File

@ -2,10 +2,9 @@ package server
import (
"kra/internal/server/handler"
"kra/internal/worker"
"github.com/google/wire"
)
// ProviderSet is server providers.
var ProviderSet = wire.NewSet(NewGinEngine, NewGinServer, handler.NewAuthority, handler.NewMenu, handler.NewAPI, handler.NewPermission, handler.NewOrganization, handler.NewAnnouncement, handler.NewEmail, handler.NewTask, handler.NewMedia, handler.NewAudit, handler.NewExport, handler.NewVersion, handler.NewDictionary, handler.NewParameter, handler.NewAPIToken, handler.NewSystemConfig, handler.NewPublic, handler.NewUser, handler.NewNavigation, handler.NewSession, handler.NewSet, worker.NewTaskExecutor, worker.NewTaskScheduler, worker.NewTaskRuntime)
var ProviderSet = wire.NewSet(NewGinEngine, NewGinServer, handler.NewAuthority, handler.NewMenu, handler.NewAPI, handler.NewPermission, handler.NewOrganization, handler.NewAnnouncement, handler.NewEmail, handler.NewTask, handler.NewMedia, handler.NewAudit, handler.NewExport, handler.NewVersion, handler.NewDictionary, handler.NewParameter, handler.NewAPIToken, handler.NewSystemConfig, handler.NewPublic, handler.NewUser, handler.NewNavigation, handler.NewSession, handler.NewSet)

View File

@ -1,122 +0,0 @@
package service
import (
"context"
"kra/internal/biz"
"kra/internal/service/dto"
)
type AuthorityService struct{ uc *biz.AuthorityUsecase }
func NewAuthorityService(uc *biz.AuthorityUsecase) *AuthorityService {
return &AuthorityService{uc: uc}
}
type PermissionService struct{ uc *biz.PermissionUsecase }
func NewPermissionService(uc *biz.PermissionUsecase) *PermissionService {
return &PermissionService{uc: uc}
}
type AccessControlService struct{ uc *biz.AccessControlUsecase }
func NewAccessControlService(uc *biz.AccessControlUsecase) *AccessControlService {
return &AccessControlService{uc: uc}
}
func (s *AccessControlService) Authorize(ctx context.Context, aid uint, path, method string) (bool, error) {
return s.uc.Authorize(ctx, aid, path, method)
}
func authorityDTO(v *biz.Authority) map[string]any {
out := convertAuthority(*v)
children := make([]map[string]any, 0, len(v.Children))
for _, child := range v.Children {
children = append(children, authorityDTO(child))
}
out["children"] = children
return out
}
func (s *AuthorityService) Authorities(ctx context.Context) ([]map[string]any, error) {
items, err := s.uc.AuthorityTree(ctx)
if err != nil {
return nil, err
}
roots := make([]map[string]any, 0, len(items))
for _, v := range items {
roots = append(roots, authorityDTO(v))
}
return roots, nil
}
func (s *AuthorityService) CreateAuthority(ctx context.Context, v *biz.Authority) error {
return s.uc.CreateAuthority(ctx, v)
}
func (s *AuthorityService) CopyAuthority(ctx context.Context, sourceID uint, v *biz.Authority) error {
return s.uc.CopyAuthority(ctx, sourceID, v)
}
func (s *AuthorityService) UpdateAuthority(ctx context.Context, v *biz.Authority) error {
return s.uc.UpdateAuthority(ctx, v)
}
func (s *AuthorityService) DeleteAuthority(ctx context.Context, id uint) error {
return s.uc.DeleteAuthority(ctx, id)
}
func authorityDomain(req *dto.AuthorityRequest) *biz.Authority {
return &biz.Authority{AuthorityID: req.AuthorityID, AuthorityName: req.AuthorityName, ParentID: req.ParentID, DataScope: req.DataScope, DefaultRouter: req.DefaultRouter}
}
func authorityResponse(value *biz.Authority) *dto.AuthorityResponse {
return &dto.AuthorityResponse{AuthorityID: value.AuthorityID, AuthorityName: value.AuthorityName, ParentID: value.ParentID, DataScope: value.DataScope, DefaultRouter: value.DefaultRouter}
}
func (s *AuthorityService) CreateAuthorityRequest(ctx context.Context, req *dto.AuthorityRequest) (*dto.AuthorityResponse, error) {
value := authorityDomain(req)
if err := s.CreateAuthority(ctx, value); err != nil {
return nil, err
}
return authorityResponse(value), nil
}
func (s *AuthorityService) CopyAuthorityRequest(ctx context.Context, req *dto.CopyAuthorityRequest) (*dto.AuthorityResponse, error) {
value := authorityDomain(&req.Authority)
if err := s.CopyAuthority(ctx, req.OldAuthorityID, value); err != nil {
return nil, err
}
return authorityResponse(value), nil
}
func (s *AuthorityService) UpdateAuthorityRequest(ctx context.Context, req *dto.AuthorityRequest) (*dto.AuthorityResponse, error) {
value := authorityDomain(req)
if err := s.UpdateAuthority(ctx, value); err != nil {
return nil, err
}
return authorityResponse(value), nil
}
func (s *AuthorityService) SetAuthorityUsers(ctx context.Context, id uint, ids []uint) error {
return s.uc.SetAuthorityUsers(ctx, id, ids)
}
func (s *AuthorityService) AuthorityUserIDs(ctx context.Context, id uint) ([]uint, error) {
return s.uc.AuthorityUserIDs(ctx, id)
}
func (s *AuthorityService) SetDataScope(ctx context.Context, id uint, scope int, ids []uint) error {
return s.uc.SetDataScope(ctx, id, scope, ids)
}
func (s *AuthorityService) DataScopeDepartmentIDs(ctx context.Context, id uint) ([]uint, error) {
return s.uc.DataScopeDepartmentIDs(ctx, id)
}
func (s *AccessControlService) ResolveDataScope(ctx context.Context, authorityID, userID uint) (biz.DataScope, error) {
return s.uc.ResolveDataScope(ctx, authorityID, userID)
}
func (s *AccessControlService) ContextWithDataScope(ctx context.Context, authorityID, userID uint) (context.Context, error) {
scope, err := s.ResolveDataScope(ctx, authorityID, userID)
if err != nil {
return ctx, err
}
return biz.NewDataScopeContext(ctx, scope), nil
}
func (s *PermissionService) SelectedButtons(ctx context.Context, aid, menuID uint) ([]uint, error) {
return s.uc.SelectedButtons(ctx, aid, menuID)
}
func (s *PermissionService) SetSelectedButtons(ctx context.Context, aid, menuID uint, ids []uint) error {
return s.uc.SetSelectedButtons(ctx, aid, menuID, ids)
}
func (s *PermissionService) CanRemoveButton(ctx context.Context, id uint) (bool, error) {
return s.uc.CanRemoveButton(ctx, id)
}

View File

@ -0,0 +1,29 @@
package service
import (
"context"
"kra/internal/biz"
)
type AccessControlService struct{ uc *biz.AccessControlUsecase }
func NewAccessControlService(uc *biz.AccessControlUsecase) *AccessControlService {
return &AccessControlService{uc: uc}
}
func (s *AccessControlService) Authorize(ctx context.Context, authorityID uint, path, method string) (bool, error) {
return s.uc.Authorize(ctx, authorityID, path, method)
}
func (s *AccessControlService) ResolveDataScope(ctx context.Context, authorityID, userID uint) (biz.DataScope, error) {
return s.uc.ResolveDataScope(ctx, authorityID, userID)
}
func (s *AccessControlService) ContextWithDataScope(ctx context.Context, authorityID, userID uint) (context.Context, error) {
scope, err := s.ResolveDataScope(ctx, authorityID, userID)
if err != nil {
return ctx, err
}
return biz.NewDataScopeContext(ctx, scope), nil
}

View File

@ -6,6 +6,7 @@ import (
"time"
"kra/internal/biz"
"kra/internal/service/dto"
)
type AnnouncementInput struct {
@ -22,15 +23,12 @@ func NewAnnouncementService(uc *biz.AnnouncementUsecase) *AnnouncementService {
return &AnnouncementService{uc: uc}
}
func announcementDTO(item *biz.Announcement) map[string]any {
func announcementDTO(item *biz.Announcement) *dto.AnnouncementResponse {
attachments := any([]any{})
if len(item.Attachments) > 0 {
_ = json.Unmarshal(item.Attachments, &attachments)
}
return map[string]any{
"ID": item.ID, "CreatedAt": item.CreatedAt, "UpdatedAt": item.UpdatedAt, "DeletedAt": nil,
"title": item.Title, "content": item.Content, "userID": item.UserID, "attachments": attachments,
}
return &dto.AnnouncementResponse{ID: item.ID, CreatedAt: item.CreatedAt, UpdatedAt: item.UpdatedAt, DeletedAt: nil, Title: item.Title, Content: item.Content, UserID: item.UserID, Attachments: attachments}
}
func announcementDO(in AnnouncementInput) *biz.Announcement {
@ -53,7 +51,7 @@ func (s *AnnouncementService) DeleteByIDs(ctx context.Context, ids []uint) error
return s.uc.DeleteByIDs(ctx, ids)
}
func (s *AnnouncementService) Find(ctx context.Context, id uint) (map[string]any, error) {
func (s *AnnouncementService) Find(ctx context.Context, id uint) (*dto.AnnouncementResponse, error) {
item, err := s.uc.Find(ctx, id)
if err != nil {
return nil, err
@ -61,26 +59,26 @@ func (s *AnnouncementService) Find(ctx context.Context, id uint) (map[string]any
return announcementDTO(item), nil
}
func (s *AnnouncementService) List(ctx context.Context, page, pageSize int, start, end *time.Time) ([]map[string]any, int64, error) {
func (s *AnnouncementService) List(ctx context.Context, page, pageSize int, start, end *time.Time) ([]*dto.AnnouncementResponse, int64, error) {
items, total, err := s.uc.List(ctx, biz.AnnouncementFilter{Page: page, PageSize: pageSize, StartCreatedAt: start, EndCreatedAt: end})
if err != nil {
return nil, 0, err
}
result := make([]map[string]any, 0, len(items))
result := make([]*dto.AnnouncementResponse, 0, len(items))
for _, item := range items {
result = append(result, announcementDTO(item))
}
return result, total, nil
}
func (s *AnnouncementService) UserOptions(ctx context.Context) ([]map[string]any, error) {
func (s *AnnouncementService) UserOptions(ctx context.Context) ([]*dto.SelectOptionResponse, error) {
items, err := s.uc.UserOptions(ctx)
if err != nil {
return nil, err
}
result := make([]map[string]any, 0, len(items))
result := make([]*dto.SelectOptionResponse, 0, len(items))
for _, item := range items {
result = append(result, map[string]any{"label": item.Label, "value": item.Value})
result = append(result, &dto.SelectOptionResponse{Label: item.Label, Value: item.Value})
}
return result, nil
}

View File

@ -147,9 +147,6 @@ func (s *APIService) SyncAPIResponses(ctx context.Context, routes []dto.APIReque
return s.SyncAPIs(ctx, items)
}
func apiDTO(v *biz.API) map[string]any {
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "path": v.Path, "description": v.Description, "apiGroup": v.APIGroup, "method": v.Method}
}
func (s *APIService) DeleteAPIs(ctx context.Context, ids []uint) error {
return s.uc.DeleteAPIs(ctx, ids)
}

View File

@ -5,6 +5,7 @@ import (
"time"
"kra/internal/biz"
"kra/internal/service/dto"
)
type TokenService struct {
@ -16,12 +17,12 @@ func NewTokenService(uc *biz.TokenUsecase, issuer biz.TokenIssuer) *TokenService
return &TokenService{uc: uc, issuer: issuer}
}
func tokenDTO(v *biz.APIToken) map[string]any {
var user any = nil
func tokenDTO(v *biz.APIToken) *dto.APITokenResponse {
var user *dto.UserResponse
if v.User != nil {
user = convertUser(v.User)
}
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "userId": v.UserID, "authorityId": v.AuthorityID, "token": v.Token, "status": v.Status, "expiresAt": v.ExpiresAt, "remark": v.Remark, "user": user}
return &dto.APITokenResponse{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, DeletedAt: nil, UserID: v.UserID, AuthorityID: v.AuthorityID, Token: v.Token, Status: v.Status, ExpiresAt: v.ExpiresAt, Remark: v.Remark, User: user}
}
func (s *TokenService) CreateAPIToken(ctx context.Context, userID, authorityID uint, days int, remark string) (string, error) {
@ -39,12 +40,12 @@ func (s *TokenService) CreateAPIToken(ctx context.Context, userID, authorityID u
}
return issued.Value, nil
}
func (s *TokenService) APITokens(ctx context.Context, page, size int, userID uint, status *bool) ([]map[string]any, int64, error) {
func (s *TokenService) APITokens(ctx context.Context, page, size int, userID uint, status *bool) ([]*dto.APITokenResponse, int64, error) {
items, total, err := s.uc.ListAPITokens(ctx, page, size, userID, status)
if err != nil {
return nil, 0, err
}
out := make([]map[string]any, 0, len(items))
out := make([]*dto.APITokenResponse, 0, len(items))
for _, v := range items {
out = append(out, tokenDTO(v))
}

View File

@ -21,28 +21,28 @@ func (s *AuditRecorder) RecordOperation(ctx context.Context, v *biz.OperationRec
func (s *AuditRecorder) RecordOperationRequest(ctx context.Context, value *dto.OperationRecordRequest) error {
return s.RecordOperation(ctx, &biz.OperationRecord{IP: value.IP, Method: value.Method, Path: value.Path, Status: value.Status, LatencyMS: value.LatencyMS, Agent: value.Agent, ErrorMessage: value.ErrorMessage, Body: value.Body, Response: value.Response, UserID: value.UserID, RequestID: value.RequestID, TraceID: value.TraceID, DeviceID: value.DeviceID})
}
func (s *AuditService) OperationsFilter(ctx context.Context, page, size int, path, method string, status int) ([]map[string]any, int64, error) {
func (s *AuditService) OperationsFilter(ctx context.Context, page, size int, path, method string, status int) ([]*dto.OperationRecordResponse, int64, error) {
return s.Operations(ctx, page, size, &biz.OperationRecord{Path: path, Method: method, Status: status})
}
func opDTO(v *biz.OperationRecord) map[string]any {
user := map[string]any{}
func opDTO(v *biz.OperationRecord) *dto.OperationRecordResponse {
var user any = map[string]any{}
if v.User != nil {
user = convertUser(v.User)
}
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "DeletedAt": nil, "ip": v.IP, "method": v.Method, "path": v.Path, "status": v.Status, "latency_ms": v.LatencyMS, "agent": v.Agent, "error_message": v.ErrorMessage, "body": v.Body, "resp": v.Response, "user_id": v.UserID, "request_id": v.RequestID, "trace_id": v.TraceID, "device_id": v.DeviceID, "user": user}
return &dto.OperationRecordResponse{ID: v.ID, CreatedAt: v.CreatedAt, DeletedAt: nil, 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, User: user}
}
func (s *AuditService) Operations(ctx context.Context, page, size int, q *biz.OperationRecord) ([]map[string]any, int64, error) {
func (s *AuditService) Operations(ctx context.Context, page, size int, q *biz.OperationRecord) ([]*dto.OperationRecordResponse, int64, error) {
items, total, err := s.uc.ListOperations(ctx, page, size, q)
if err != nil {
return nil, 0, err
}
out := make([]map[string]any, 0, len(items))
out := make([]*dto.OperationRecordResponse, 0, len(items))
for _, v := range items {
out = append(out, opDTO(v))
}
return out, total, nil
}
func (s *AuditService) Operation(ctx context.Context, id uint) (map[string]any, error) {
func (s *AuditService) Operation(ctx context.Context, id uint) (*dto.OperationRecordResponse, error) {
v, err := s.uc.FindOperation(ctx, id)
if err != nil {
return nil, err
@ -59,28 +59,28 @@ func (s *AuditRecorder) RecordLogin(ctx context.Context, v *biz.LoginLog) error
func (s *AuditRecorder) RecordLoginRequest(ctx context.Context, value *dto.LoginLogRequest) error {
return s.RecordLogin(ctx, &biz.LoginLog{Username: value.Username, IP: value.IP, Status: value.Status, ErrorMessage: value.ErrorMessage, Agent: value.Agent, UserID: value.UserID})
}
func (s *AuditService) LoginsFilter(ctx context.Context, page, size int, username string, status bool) ([]map[string]any, int64, error) {
func (s *AuditService) LoginsFilter(ctx context.Context, page, size int, username string, status bool) ([]*dto.LoginLogResponse, int64, error) {
return s.Logins(ctx, page, size, &biz.LoginLog{Username: username, Status: status, FilterByStatus: status})
}
func loginDTO(v *biz.LoginLog) map[string]any {
user := map[string]any{}
func loginDTO(v *biz.LoginLog) *dto.LoginLogResponse {
var user any = map[string]any{}
if v.User != nil {
user = convertUser(v.User)
}
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "DeletedAt": nil, "username": v.Username, "ip": v.IP, "status": v.Status, "errorMessage": v.ErrorMessage, "agent": v.Agent, "userId": v.UserID, "user": user}
return &dto.LoginLogResponse{ID: v.ID, CreatedAt: v.CreatedAt, DeletedAt: nil, Username: v.Username, IP: v.IP, Status: v.Status, ErrorMessage: v.ErrorMessage, Agent: v.Agent, UserID: v.UserID, User: user}
}
func (s *AuditService) Logins(ctx context.Context, page, size int, q *biz.LoginLog) ([]map[string]any, int64, error) {
func (s *AuditService) Logins(ctx context.Context, page, size int, q *biz.LoginLog) ([]*dto.LoginLogResponse, int64, error) {
items, total, err := s.uc.ListLogins(ctx, page, size, q)
if err != nil {
return nil, 0, err
}
out := make([]map[string]any, 0, len(items))
out := make([]*dto.LoginLogResponse, 0, len(items))
for _, v := range items {
out = append(out, loginDTO(v))
}
return out, total, nil
}
func (s *AuditService) Login(ctx context.Context, id uint) (map[string]any, error) {
func (s *AuditService) Login(ctx context.Context, id uint) (*dto.LoginLogResponse, error) {
v, err := s.uc.FindLogin(ctx, id)
if err != nil {
return nil, err
@ -97,18 +97,18 @@ func (s *AuditRecorder) RecordDataAccess(ctx context.Context, v *biz.DataAccessL
func (s *AuditRecorder) RecordDataAccessRequest(ctx context.Context, value *dto.DataAccessRecordRequest) error {
return s.RecordDataAccess(ctx, &biz.DataAccessLog{EventType: value.EventType, Operation: value.Operation, UserID: value.UserID, AuthorityID: value.AuthorityID, RequestID: value.RequestID, Method: value.Method, Path: value.Path, Detail: value.Detail})
}
func (s *AuditService) DataAccessRequest(ctx context.Context, req *dto.DataAccessListRequest) ([]map[string]any, int64, error) {
func (s *AuditService) DataAccessRequest(ctx context.Context, req *dto.DataAccessListRequest) ([]*dto.DataAccessLogResponse, int64, error) {
return s.DataAccess(ctx, req.Page, req.PageSize, &biz.DataAccessLog{EventType: req.EventType, TargetTable: req.TargetTable, UserID: req.UserID})
}
func dataAccessDTO(v *biz.DataAccessLog) map[string]any {
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "DeletedAt": nil, "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 dataAccessDTO(v *biz.DataAccessLog) *dto.DataAccessLogResponse {
return &dto.DataAccessLogResponse{ID: v.ID, CreatedAt: v.CreatedAt, DeletedAt: nil, 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 (s *AuditService) DataAccess(ctx context.Context, page, size int, q *biz.DataAccessLog) ([]map[string]any, int64, error) {
func (s *AuditService) DataAccess(ctx context.Context, page, size int, q *biz.DataAccessLog) ([]*dto.DataAccessLogResponse, int64, error) {
items, total, err := s.uc.ListDataAccess(ctx, page, size, q)
if err != nil {
return nil, 0, err
}
out := make([]map[string]any, 0, len(items))
out := make([]*dto.DataAccessLogResponse, 0, len(items))
for _, v := range items {
out = append(out, dataAccessDTO(v))
}

View File

@ -8,8 +8,8 @@ import (
"kra/internal/service/dto"
)
func errorDTO(v *biz.ErrorRecord) map[string]any {
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "DeletedAt": nil, "form": v.Form, "info": v.Info, "level": v.Level, "request_id": v.RequestID, "trace_id": v.TraceID, "solution": v.Solution, "status": v.Status}
func errorDTO(v *biz.ErrorRecord) *dto.ErrorRecordResponse {
return &dto.ErrorRecordResponse{ID: v.ID, CreatedAt: v.CreatedAt, DeletedAt: nil, Form: v.Form, Info: v.Info, Level: v.Level, RequestID: v.RequestID, TraceID: v.TraceID, Solution: v.Solution, Status: v.Status}
}
func (s *AuditRecorder) CreateError(ctx context.Context, v *biz.ErrorRecord) error {
return s.uc.CreateError(ctx, v)
@ -23,7 +23,7 @@ func (s *AuditRecorder) CreateErrorRequest(ctx context.Context, req *dto.ErrorRe
func (s *AuditService) UpdateErrorRequest(ctx context.Context, req *dto.ErrorRecordRequest) error {
return s.UpdateError(ctx, errorDomain(req))
}
func (s *AuditService) ErrorsFilter(ctx context.Context, page, size int, form, info string, createdAtRange []time.Time) ([]map[string]any, int64, error) {
func (s *AuditService) ErrorsFilter(ctx context.Context, page, size int, form, info string, createdAtRange []time.Time) ([]*dto.ErrorRecordResponse, int64, error) {
return s.Errors(ctx, page, size, &biz.ErrorRecord{Form: form, Info: info, CreatedAtRange: createdAtRange})
}
func (s *AuditService) UpdateError(ctx context.Context, v *biz.ErrorRecord) error {
@ -32,19 +32,19 @@ func (s *AuditService) UpdateError(ctx context.Context, v *biz.ErrorRecord) erro
func (s *AuditService) DeleteErrors(ctx context.Context, ids []uint) error {
return s.uc.DeleteErrors(ctx, ids)
}
func (s *AuditService) Error(ctx context.Context, id uint) (map[string]any, error) {
func (s *AuditService) Error(ctx context.Context, id uint) (*dto.ErrorRecordResponse, error) {
v, err := s.uc.FindError(ctx, id)
if err != nil {
return nil, err
}
return errorDTO(v), nil
}
func (s *AuditService) Errors(ctx context.Context, page, size int, q *biz.ErrorRecord) ([]map[string]any, int64, error) {
func (s *AuditService) Errors(ctx context.Context, page, size int, q *biz.ErrorRecord) ([]*dto.ErrorRecordResponse, int64, error) {
items, total, err := s.uc.ListErrors(ctx, page, size, q)
if err != nil {
return nil, 0, err
}
out := make([]map[string]any, 0, len(items))
out := make([]*dto.ErrorRecordResponse, 0, len(items))
for _, v := range items {
out = append(out, errorDTO(v))
}

View File

@ -4,6 +4,7 @@ import (
"context"
"kra/internal/biz"
"kra/internal/service/dto"
)
type LogViewerService struct{ uc *biz.LogViewerUsecase }
@ -12,32 +13,32 @@ func NewLogViewerService(uc *biz.LogViewerUsecase) *LogViewerService {
return &LogViewerService{uc: uc}
}
func (s *LogViewerService) LogDates(ctx context.Context, month string) (map[string]any, error) {
func (s *LogViewerService) LogDates(ctx context.Context, month string) (*dto.LogDatesResponse, error) {
items, err := s.uc.LogDates(ctx, month)
if err != nil {
return nil, err
}
out := make([]map[string]any, 0, len(items))
out := make([]*dto.LogDateResponse, 0, len(items))
for _, v := range items {
out = append(out, map[string]any{"date": v.Date, "fileCount": v.FileCount})
out = append(out, &dto.LogDateResponse{Date: v.Date, FileCount: v.FileCount})
}
return map[string]any{"month": month, "dates": out}, nil
return &dto.LogDatesResponse{Month: month, Dates: out}, nil
}
func (s *LogViewerService) LogFiles(ctx context.Context, date string) (map[string]any, error) {
func (s *LogViewerService) LogFiles(ctx context.Context, date string) (*dto.LogFilesResponse, error) {
items, err := s.uc.LogFiles(ctx, date)
if err != nil {
return nil, err
}
out := make([]map[string]any, 0, len(items))
out := make([]*dto.LogFileResponse, 0, len(items))
for _, v := range items {
out = append(out, map[string]any{"path": v.Path, "name": v.Name, "size": v.Size, "modifiedAt": v.ModifiedAt})
out = append(out, &dto.LogFileResponse{Path: v.Path, Name: v.Name, Size: v.Size, ModifiedAt: v.ModifiedAt})
}
return map[string]any{"date": date, "files": out}, nil
return &dto.LogFilesResponse{Date: date, Files: out}, nil
}
func (s *LogViewerService) LogContent(ctx context.Context, date, path string, cursor *int64) (map[string]any, error) {
func (s *LogViewerService) LogContent(ctx context.Context, date, path string, cursor *int64) (*dto.LogContentResponse, error) {
v, err := s.uc.LogContent(ctx, date, path, cursor)
if err != nil {
return nil, err
}
return map[string]any{"date": v.Date, "path": v.Path, "content": v.Content, "lineCount": v.LineCount, "nextCursor": v.NextCursor, "hasMore": v.HasMore, "limitedByBytes": v.LimitedByBytes, "size": v.Size, "modifiedAt": v.ModifiedAt}, nil
return &dto.LogContentResponse{Date: v.Date, Path: v.Path, Content: v.Content, LineCount: v.LineCount, NextCursor: v.NextCursor, HasMore: v.HasMore, LimitedByBytes: v.LimitedByBytes, Size: v.Size, ModifiedAt: v.ModifiedAt}, nil
}

View File

@ -8,10 +8,10 @@ import (
)
type LoginResult struct {
User map[string]any `json:"user"`
Token string `json:"token"`
ExpiresAt int64 `json:"expiresAt"`
NeedChangePassword bool `json:"needChangePassword"`
User *dto.UserResponse `json:"user"`
Token string `json:"token"`
ExpiresAt int64 `json:"expiresAt"`
NeedChangePassword bool `json:"needChangePassword"`
}
type AuthService struct {
@ -41,3 +41,7 @@ func (s *AuthService) SwitchAuthority(ctx context.Context, id, authorityID uint)
}
return loginResult(value), nil
}
func (s *AuthService) AuthenticateToken(ctx context.Context, token string) (*biz.TokenAuthentication, error) {
return s.uc.AuthenticateToken(ctx, token)
}

View File

@ -0,0 +1,88 @@
package service
import (
"context"
"kra/internal/biz"
"kra/internal/service/dto"
)
type AuthorityService struct{ uc *biz.AuthorityUsecase }
func NewAuthorityService(uc *biz.AuthorityUsecase) *AuthorityService {
return &AuthorityService{uc: uc}
}
func authorityDTO(value *biz.Authority) *dto.AuthorityResponse {
out := convertAuthority(*value)
children := make([]*dto.AuthorityResponse, 0, len(value.Children))
for _, child := range value.Children {
children = append(children, authorityDTO(child))
}
out.Children = children
return out
}
func authorityDomain(req *dto.AuthorityRequest) *biz.Authority {
return &biz.Authority{AuthorityID: req.AuthorityID, AuthorityName: req.AuthorityName, ParentID: req.ParentID, DataScope: req.DataScope, DefaultRouter: req.DefaultRouter}
}
func authorityResponse(value *biz.Authority) *dto.AuthorityResponse {
return &dto.AuthorityResponse{AuthorityID: value.AuthorityID, AuthorityName: value.AuthorityName, ParentID: value.ParentID, DataScope: value.DataScope, DefaultRouter: value.DefaultRouter}
}
func (s *AuthorityService) Authorities(ctx context.Context) ([]*dto.AuthorityResponse, error) {
items, err := s.uc.AuthorityTree(ctx)
if err != nil {
return nil, err
}
roots := make([]*dto.AuthorityResponse, 0, len(items))
for _, value := range items {
roots = append(roots, authorityDTO(value))
}
return roots, nil
}
func (s *AuthorityService) CreateAuthorityRequest(ctx context.Context, req *dto.AuthorityRequest) (*dto.AuthorityResponse, error) {
value := authorityDomain(req)
if err := s.uc.CreateAuthority(ctx, value); err != nil {
return nil, err
}
return authorityResponse(value), nil
}
func (s *AuthorityService) CopyAuthorityRequest(ctx context.Context, req *dto.CopyAuthorityRequest) (*dto.AuthorityResponse, error) {
value := authorityDomain(&req.Authority)
if err := s.uc.CopyAuthority(ctx, req.OldAuthorityID, value); err != nil {
return nil, err
}
return authorityResponse(value), nil
}
func (s *AuthorityService) UpdateAuthorityRequest(ctx context.Context, req *dto.AuthorityRequest) (*dto.AuthorityResponse, error) {
value := authorityDomain(req)
if err := s.uc.UpdateAuthority(ctx, value); err != nil {
return nil, err
}
return authorityResponse(value), nil
}
func (s *AuthorityService) DeleteAuthority(ctx context.Context, id uint) error {
return s.uc.DeleteAuthority(ctx, id)
}
func (s *AuthorityService) SetAuthorityUsers(ctx context.Context, id uint, ids []uint) error {
return s.uc.SetAuthorityUsers(ctx, id, ids)
}
func (s *AuthorityService) AuthorityUserIDs(ctx context.Context, id uint) ([]uint, error) {
return s.uc.AuthorityUserIDs(ctx, id)
}
func (s *AuthorityService) SetDataScope(ctx context.Context, id uint, scope int, ids []uint) error {
return s.uc.SetDataScope(ctx, id, scope, ids)
}
func (s *AuthorityService) DataScopeDepartmentIDs(ctx context.Context, id uint) ([]uint, error) {
return s.uc.DataScopeDepartmentIDs(ctx, id)
}

View File

@ -20,7 +20,7 @@ func dictionaryDomain(value *dto.DictionaryRequest) *biz.Dictionary {
}
return &biz.Dictionary{ID: value.ID, Name: value.Name, Type: value.Type, Status: status, Desc: value.Description, ParentID: value.ParentID}
}
func (s *DictionaryService) CreateDictionaryRequest(ctx context.Context, req *dto.DictionaryRequest) (map[string]any, error) {
func (s *DictionaryService) CreateDictionaryRequest(ctx context.Context, req *dto.DictionaryRequest) (*dto.DictionaryResponse, error) {
value := dictionaryDomain(req)
if err := s.CreateDictionary(ctx, value); err != nil {
return nil, err
@ -30,41 +30,51 @@ func (s *DictionaryService) CreateDictionaryRequest(ctx context.Context, req *dt
func (s *DictionaryService) UpdateDictionaryRequest(ctx context.Context, req *dto.DictionaryRequest) error {
return s.UpdateDictionary(ctx, dictionaryDomain(req))
}
func dictionaryDTO(v *biz.Dictionary) map[string]any {
children := make([]map[string]any, 0, len(v.Children))
func dictionaryDTO(v *biz.Dictionary) *dto.DictionaryResponse {
children := make([]*dto.DictionaryResponse, 0, len(v.Children))
for _, x := range v.Children {
children = append(children, dictionaryDTO(x))
}
details := make([]map[string]any, 0, len(v.Details))
details := make([]*dto.DictionaryDetailResponse, 0, len(v.Details))
for _, x := range v.Details {
details = append(details, detailDTO(x))
}
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "name": v.Name, "type": v.Type, "status": v.Status, "desc": v.Desc, "parentID": v.ParentID, "children": children, "sysDictionaryDetails": details}
return &dto.DictionaryResponse{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, DeletedAt: nil, Name: v.Name, Type: v.Type, Status: v.Status, Desc: v.Desc, ParentID: v.ParentID, Children: children, SysDictionaryDetails: details}
}
func (s *DictionaryService) Dictionaries(ctx context.Context, page, size int, name, typ string, details bool) ([]map[string]any, int64, error) {
func (s *DictionaryService) Dictionaries(ctx context.Context, page, size int, name, typ string, details bool) ([]*dto.DictionaryResponse, int64, error) {
items, total, err := s.uc.ListDictionaries(ctx, page, size, name, typ, details)
if err != nil {
return nil, 0, err
}
out := make([]map[string]any, 0, len(items))
out := make([]*dto.DictionaryResponse, 0, len(items))
for _, v := range items {
out = append(out, dictionaryDTO(v))
}
return out, total, nil
}
func (s *DictionaryService) Dictionary(ctx context.Context, id uint, typ string, status *bool, details bool) (map[string]any, error) {
func (s *DictionaryService) Dictionary(ctx context.Context, id uint, typ string, status *bool, details bool) (*dto.DictionaryResponse, error) {
v, err := s.uc.FindDictionary(ctx, id, typ, status, details)
if err != nil {
return nil, err
}
return dictionaryDTO(v), nil
}
func (s *DictionaryService) ExportDictionary(ctx context.Context, id uint) (map[string]any, error) {
func (s *DictionaryService) ExportDictionary(ctx context.Context, id uint) (*dto.DictionaryExportResponse, error) {
v, err := s.uc.ExportDictionary(ctx, id)
if err != nil {
return nil, err
}
return dictionaryDTO(v), nil
details := make([]*dto.DictionaryDetailExportResponse, 0, len(v.Details))
for _, detail := range v.Details {
details = append(details, &dto.DictionaryDetailExportResponse{
Label: detail.Label, Value: detail.Value, Extend: detail.Extend,
Status: detail.Status, Sort: detail.Sort, Level: detail.Level, Path: detail.Path,
})
}
return &dto.DictionaryExportResponse{
Name: v.Name, Type: v.Type, Status: v.Status, Desc: v.Desc,
SysDictionaryDetails: details,
}, nil
}
func (s *DictionaryService) CreateDictionary(ctx context.Context, v *biz.Dictionary) error {
return s.uc.CreateDictionary(ctx, v)
@ -89,37 +99,37 @@ func (s *DictionaryService) CreateDictionaryDetailRequest(ctx context.Context, r
func (s *DictionaryService) UpdateDictionaryDetailRequest(ctx context.Context, req *dto.DictionaryDetailRequest) error {
return s.UpdateDictionaryDetail(ctx, detailDomain(req))
}
func detailDTO(v *biz.DictionaryDetail) map[string]any {
children := make([]map[string]any, 0, len(v.Children))
func detailDTO(v *biz.DictionaryDetail) *dto.DictionaryDetailResponse {
children := make([]*dto.DictionaryDetailResponse, 0, len(v.Children))
for _, x := range v.Children {
children = append(children, detailDTO(x))
}
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "label": v.Label, "value": v.Value, "extend": v.Extend, "status": v.Status, "sort": v.Sort, "sysDictionaryID": v.DictionaryID, "parentID": v.ParentID, "level": v.Level, "path": v.Path, "disabled": !v.Status, "children": children}
return &dto.DictionaryDetailResponse{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, DeletedAt: nil, Label: v.Label, Value: v.Value, Extend: v.Extend, Status: v.Status, Sort: v.Sort, DictionaryID: v.DictionaryID, ParentID: v.ParentID, Level: v.Level, Path: v.Path, Disabled: !v.Status, Children: children}
}
func (s *DictionaryService) DictionaryDetails(ctx context.Context, page, size int, filter biz.DictionaryDetailFilter) ([]map[string]any, int64, error) {
func (s *DictionaryService) DictionaryDetails(ctx context.Context, page, size int, filter biz.DictionaryDetailFilter) ([]*dto.DictionaryDetailResponse, int64, error) {
items, total, err := s.uc.ListDictionaryDetails(ctx, page, size, filter)
if err != nil {
return nil, 0, err
}
out := make([]map[string]any, 0, len(items))
out := make([]*dto.DictionaryDetailResponse, 0, len(items))
for _, v := range items {
out = append(out, detailDTO(v))
}
return out, total, nil
}
func (s *DictionaryService) DictionaryDetail(ctx context.Context, id uint) (map[string]any, error) {
func (s *DictionaryService) DictionaryDetail(ctx context.Context, id uint) (*dto.DictionaryDetailResponse, error) {
v, err := s.uc.FindDictionaryDetail(ctx, id)
if err != nil {
return nil, err
}
return detailDTO(v), nil
}
func (s *DictionaryService) DictionaryTree(ctx context.Context, id uint, typ string) ([]map[string]any, error) {
func (s *DictionaryService) DictionaryTree(ctx context.Context, id uint, typ string) ([]*dto.DictionaryDetailResponse, error) {
items, err := s.uc.DictionaryDetailTree(ctx, id, typ)
if err != nil {
return nil, err
}
out := make([]map[string]any, 0, len(items))
out := make([]*dto.DictionaryDetailResponse, 0, len(items))
for _, v := range items {
out = append(out, detailDTO(v))
}

View File

@ -27,12 +27,12 @@ func (s *DictionaryService) ImportDictionaryJSON(ctx context.Context, raw string
}
return s.uc.ImportDictionary(ctx, dictionary, details)
}
func (s *DictionaryService) DictionaryDetailsByParent(ctx context.Context, dictionaryID uint, parentID *uint, includeChildren bool) ([]map[string]any, error) {
func (s *DictionaryService) DictionaryDetailsByParent(ctx context.Context, dictionaryID uint, parentID *uint, includeChildren bool) ([]*dto.DictionaryDetailResponse, error) {
items, err := s.uc.DictionaryDetailsByParent(ctx, dictionaryID, parentID, includeChildren)
if err != nil {
return nil, err
}
out := make([]map[string]any, 0, len(items))
out := make([]*dto.DictionaryDetailResponse, 0, len(items))
for _, item := range items {
out = append(out, detailDTO(item))
}

View File

@ -1,6 +1,9 @@
package dto
import "encoding/json"
import (
"encoding/json"
"time"
)
type AnnouncementRequest struct {
ID uint `json:"ID"`
@ -9,3 +12,19 @@ type AnnouncementRequest struct {
UserID *uint `json:"userID"`
Attachments json.RawMessage `json:"attachments"`
}
type AnnouncementResponse struct {
ID uint `json:"ID"`
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
DeletedAt any `json:"DeletedAt"`
Title string `json:"title"`
Content string `json:"content"`
UserID *uint `json:"userID"`
Attachments any `json:"attachments"`
}
type SelectOptionResponse struct {
Label string `json:"label"`
Value uint `json:"value"`
}

View File

@ -1,5 +1,7 @@
package dto
import "time"
type OperationRecordRequest struct {
IP string
Method string
@ -44,3 +46,99 @@ type ErrorRecordRequest struct {
Solution string `json:"solution"`
Status string `json:"status"`
}
type OperationRecordResponse struct {
ID uint `json:"ID"`
CreatedAt time.Time `json:"CreatedAt"`
DeletedAt any `json:"DeletedAt"`
IP string `json:"ip"`
Method string `json:"method"`
Path string `json:"path"`
Status int `json:"status"`
LatencyMS int64 `json:"latency_ms"`
Agent string `json:"agent"`
ErrorMessage string `json:"error_message"`
Body string `json:"body"`
Response string `json:"resp"`
UserID uint `json:"user_id"`
RequestID string `json:"request_id"`
TraceID string `json:"trace_id"`
DeviceID string `json:"device_id"`
User any `json:"user"`
}
type LoginLogResponse struct {
ID uint `json:"ID"`
CreatedAt time.Time `json:"CreatedAt"`
DeletedAt any `json:"DeletedAt"`
Username string `json:"username"`
IP string `json:"ip"`
Status bool `json:"status"`
ErrorMessage string `json:"errorMessage"`
Agent string `json:"agent"`
UserID uint `json:"userId"`
User any `json:"user"`
}
type DataAccessLogResponse struct {
ID uint `json:"ID"`
CreatedAt time.Time `json:"CreatedAt"`
DeletedAt any `json:"DeletedAt"`
EventType string `json:"eventType"`
TargetTable string `json:"targetTable"`
Operation string `json:"operation"`
UserID uint `json:"userId"`
AuthorityID uint `json:"authorityId"`
Scope int `json:"scope"`
RequestID string `json:"requestId"`
Method string `json:"method"`
Path string `json:"path"`
Detail string `json:"detail"`
}
type ErrorRecordResponse struct {
ID uint `json:"ID"`
CreatedAt time.Time `json:"CreatedAt"`
DeletedAt any `json:"DeletedAt"`
Form string `json:"form"`
Info string `json:"info"`
Level string `json:"level"`
RequestID string `json:"request_id"`
TraceID string `json:"trace_id"`
Solution string `json:"solution"`
Status string `json:"status"`
}
type LogDateResponse struct {
Date string `json:"date"`
FileCount int `json:"fileCount"`
}
type LogDatesResponse struct {
Month string `json:"month"`
Dates []*LogDateResponse `json:"dates"`
}
type LogFileResponse struct {
Path string `json:"path"`
Name string `json:"name"`
Size int64 `json:"size"`
ModifiedAt time.Time `json:"modifiedAt"`
}
type LogFilesResponse struct {
Date string `json:"date"`
Files []*LogFileResponse `json:"files"`
}
type LogContentResponse struct {
Date string `json:"date"`
Path string `json:"path"`
Content string `json:"content"`
LineCount int `json:"lineCount"`
NextCursor int64 `json:"nextCursor"`
HasMore bool `json:"hasMore"`
LimitedByBytes bool `json:"limitedByBytes"`
Size int64 `json:"size"`
ModifiedAt time.Time `json:"modifiedAt"`
}

View File

@ -27,9 +27,11 @@ type SetDataScopeRequest struct {
}
type AuthorityResponse struct {
AuthorityID uint `json:"authorityId"`
AuthorityName string `json:"authorityName"`
ParentID *uint `json:"parentId"`
DataScope int `json:"dataScope"`
DefaultRouter string `json:"defaultRouter"`
AuthorityID uint `json:"authorityId"`
AuthorityName string `json:"authorityName"`
ParentID *uint `json:"parentId"`
Children []*AuthorityResponse `json:"children"`
Menus []any `json:"menus"`
DataScope int `json:"dataScope"`
DefaultRouter string `json:"defaultRouter"`
}

View File

@ -1,5 +1,7 @@
package dto
import "time"
type ExportConditionRequest struct {
From string `json:"from"`
Column string `json:"column"`
@ -24,3 +26,33 @@ type ExportTemplateRequest struct {
Conditions []ExportConditionRequest `json:"conditions"`
Joins []ExportJoinRequest `json:"joinTemplate"`
}
type ExportConditionResponse struct {
From string `json:"from"`
Column string `json:"column"`
Operator string `json:"operator"`
}
type ExportJoinResponse struct {
Join string `json:"joins"`
Table string `json:"table"`
On string `json:"on"`
}
type ExportTemplateResponse struct {
ID uint `json:"ID"`
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
DeletedAt any `json:"DeletedAt"`
DBName string `json:"dbName"`
Name string `json:"name"`
TableName string `json:"tableName"`
TemplateID string `json:"templateID"`
TemplateInfo string `json:"templateInfo"`
SQL string `json:"sql"`
ImportSQL string `json:"importSql"`
Limit *int `json:"limit"`
Order string `json:"order"`
Conditions []ExportConditionResponse `json:"conditions"`
Joins []ExportJoinResponse `json:"joinTemplate"`
}

View File

@ -2,6 +2,46 @@ package dto
import "time"
type MediaResponse struct {
ID uint `json:"ID"`
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
DeletedAt any `json:"DeletedAt"`
Name string `json:"name"`
ClassID int `json:"classId"`
URL string `json:"url"`
Tag string `json:"tag"`
Key string `json:"key"`
Size int64 `json:"size"`
Mime string `json:"mime"`
MD5 string `json:"md5"`
UserID uint `json:"userId"`
}
type MediaCategoryResponse struct {
ID uint `json:"ID"`
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
DeletedAt any `json:"DeletedAt"`
Name string `json:"name"`
PID uint `json:"pid"`
Children []*MediaCategoryResponse `json:"children"`
}
type StorageObjectResponse struct {
Name string `json:"name"`
Key string `json:"key"`
URL string `json:"url"`
Size int64 `json:"size"`
}
type InitUploadResponse struct {
Instant bool `json:"instant"`
UploadID uint `json:"uploadId,omitempty"`
UploadedChunks []int `json:"uploadedChunks"`
Media *MediaResponse `json:"media"`
}
type MediaListRequest struct {
Page int `json:"page"`
PageSize int `json:"pageSize"`

View File

@ -1,5 +1,7 @@
package dto
import "time"
type DictionaryRequest struct {
ID uint `json:"ID" form:"ID"`
Name string `json:"name" form:"name"`
@ -47,3 +49,101 @@ type APITokenListRequest struct {
UserID uint `json:"userId"`
Status *bool `json:"status"`
}
type APITokenResponse struct {
ID uint `json:"ID"`
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
DeletedAt any `json:"DeletedAt"`
UserID uint `json:"userId"`
AuthorityID uint `json:"authorityId"`
Token string `json:"token"`
Status bool `json:"status"`
ExpiresAt time.Time `json:"expiresAt"`
Remark string `json:"remark"`
User *UserResponse `json:"user"`
}
type SecurityConfigResponse struct {
ID uint `json:"ID"`
CaptchaOpen int `json:"captchaOpen"`
CaptchaTimeout int `json:"captchaTimeout"`
KeyLong int `json:"keyLong"`
ImgWidth int `json:"imgWidth"`
ImgHeight int `json:"imgHeight"`
PwdMinLength int `json:"pwdMinLength"`
PwdRequireUpper bool `json:"pwdRequireUpper"`
PwdRequireLower bool `json:"pwdRequireLower"`
PwdRequireDigit bool `json:"pwdRequireDigit"`
PwdRequireSpecial bool `json:"pwdRequireSpecial"`
LimitEnable bool `json:"limitEnable"`
LimitWindow int `json:"limitWindow"`
LimitCount int `json:"limitCount"`
LockEnable bool `json:"lockEnable"`
LockThreshold int `json:"lockThreshold"`
LockDuration int `json:"lockDuration"`
PwdExpireEnable bool `json:"pwdExpireEnable"`
PwdExpireDays int `json:"pwdExpireDays"`
ForceNewUserChangePassword bool `json:"forceNewUserChangePassword"`
}
type SystemParameterResponse struct {
ID uint `json:"ID"`
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
DeletedAt any `json:"DeletedAt"`
Name string `json:"name"`
Key string `json:"key"`
Value string `json:"value"`
Desc string `json:"desc"`
}
type DictionaryResponse struct {
ID uint `json:"ID"`
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
DeletedAt any `json:"DeletedAt"`
Name string `json:"name"`
Type string `json:"type"`
Status bool `json:"status"`
Desc string `json:"desc"`
ParentID *uint `json:"parentID"`
Children []*DictionaryResponse `json:"children"`
SysDictionaryDetails []*DictionaryDetailResponse `json:"sysDictionaryDetails"`
}
type DictionaryDetailResponse struct {
ID uint `json:"ID"`
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
DeletedAt any `json:"DeletedAt"`
Label string `json:"label"`
Value string `json:"value"`
Extend string `json:"extend"`
Status bool `json:"status"`
Sort int `json:"sort"`
DictionaryID uint `json:"sysDictionaryID"`
ParentID *uint `json:"parentID"`
Level int `json:"level"`
Path string `json:"path"`
Disabled bool `json:"disabled"`
Children []*DictionaryDetailResponse `json:"children"`
}
type DictionaryExportResponse struct {
Name string `json:"name"`
Type string `json:"type"`
Status bool `json:"status"`
Desc string `json:"desc"`
SysDictionaryDetails []*DictionaryDetailExportResponse `json:"sysDictionaryDetails"`
}
type DictionaryDetailExportResponse struct {
Label string `json:"label"`
Value string `json:"value"`
Extend string `json:"extend"`
Status bool `json:"status"`
Sort int `json:"sort"`
Level int `json:"level"`
Path string `json:"path"`
}

View File

@ -1,5 +1,7 @@
package dto
import "time"
type LoginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
@ -71,3 +73,60 @@ type LoginLogRequest struct {
Agent string
UserID uint
}
type UserResponse struct {
ID uint `json:"ID"`
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
DeletedAt any `json:"DeletedAt"`
UUID string `json:"uuid"`
Username string `json:"userName"`
NickName string `json:"nickName"`
HeaderImg string `json:"headerImg"`
AuthorityID uint `json:"authorityId"`
Authority *AuthorityResponse `json:"authority"`
Authorities []*AuthorityResponse `json:"authorities"`
DeptID uint `json:"deptId"`
Department any `json:"dept"`
Departments []*DepartmentResponse `json:"departments"`
Positions []*PositionResponse `json:"positions"`
Phone string `json:"phone"`
Email string `json:"email"`
Enable int `json:"enable"`
OriginSetting map[string]any `json:"originSetting"`
}
type ServerInfoResponse struct {
OS ServerOSResponse `json:"os"`
CPU ServerCPUResponse `json:"cpu"`
RAM ServerRAMResponse `json:"ram"`
Disk []ServerDiskResponse `json:"disk"`
}
type ServerOSResponse struct {
GOOS string `json:"goos"`
NumCPU int `json:"numCpu"`
Compiler string `json:"compiler"`
GoVersion string `json:"goVersion"`
NumGoroutine int `json:"numGoroutine"`
}
type ServerCPUResponse struct {
CPUs []float64 `json:"cpus"`
Cores int `json:"cores"`
}
type ServerRAMResponse struct {
UsedMB uint64 `json:"usedMb"`
TotalMB uint64 `json:"totalMb"`
UsedPercent int `json:"usedPercent"`
}
type ServerDiskResponse struct {
MountPoint string `json:"mountPoint"`
UsedMB uint64 `json:"usedMb"`
UsedGB uint64 `json:"usedGb"`
TotalMB uint64 `json:"totalMb"`
TotalGB uint64 `json:"totalGb"`
UsedPercent int `json:"usedPercent"`
}

View File

@ -1,6 +1,6 @@
package dto
import "kra/internal/conf"
import "encoding/json"
type SecurityConfigRequest struct {
ID uint `json:"ID"`
@ -26,84 +26,5 @@ type SecurityConfigRequest struct {
}
type SetSystemConfigRequest struct {
Config struct {
Data *struct {
Database *struct {
Driver string `json:"driver"`
Source string `json:"source"`
Host string `json:"host"`
Port string `json:"port"`
User string `json:"user"`
Password string `json:"password"`
Name string `json:"name"`
Config string `json:"config"`
Path string `json:"path"`
Prefix string `json:"prefix"`
Engine string `json:"engine"`
LogMode string `json:"log_mode"`
MaxIdleConns int32 `json:"max_idle_conns"`
MaxOpenConns int32 `json:"max_open_conns"`
ConnMaxLifetime int32 `json:"conn_max_lifetime"`
Singular bool `json:"singular"`
} `json:"database"`
Redis *struct {
Network string `json:"network"`
Addr string `json:"addr"`
ReadTimeout string `json:"read_timeout"`
WriteTimeout string `json:"write_timeout"`
Name string `json:"name"`
Password string `json:"password"`
DB int32 `json:"db"`
UseCluster bool `json:"use_cluster"`
ClusterAddrs []string `json:"cluster_addrs"`
} `json:"redis"`
DatabaseList []*conf.Data_Database `json:"database_list"`
RedisList []*conf.Data_Redis `json:"redis_list"`
Mongo *conf.Data_Mongo `json:"mongo"`
} `json:"data"`
Admin struct {
RouterPrefix string `json:"routerPrefix"`
System struct {
UseRedis bool `json:"useRedis"`
UseMultipoint bool `json:"useMultipoint"`
UseStrictAuth bool `json:"useStrictAuth"`
DisableAutoMigrate bool `json:"disableAutoMigrate"`
UseMongo bool `json:"useMongo"`
} `json:"system"`
JWT struct {
SigningKey string `json:"signingKey"`
ExpiresTime string `json:"expiresTime"`
BufferTime string `json:"bufferTime"`
Issuer string `json:"issuer"`
} `json:"jwt"`
Captcha struct {
KeyLong int32 `json:"keyLong"`
ImgWidth int32 `json:"imgWidth"`
ImgHeight int32 `json:"imgHeight"`
StoreExpiration string `json:"storeExpiration"`
} `json:"captcha"`
Local struct {
StorePath string `json:"storePath"`
PathPrefix string `json:"pathPrefix"`
} `json:"local"`
Media struct {
SessionTTL int32 `json:"sessionTtl"`
MaxFileSize int64 `json:"maxFileSize"`
} `json:"media"`
Storage *conf.AdminBackend_Storage `json:"storage"`
Zap *conf.AdminBackend_Zap `json:"zap"`
Cors *conf.AdminBackend_CORS `json:"cors"`
App *conf.AdminBackend_App `json:"app"`
} `json:"admin"`
Email *struct {
To string `json:"to"`
From string `json:"from"`
Host string `json:"host"`
Secret string `json:"secret"`
Nickname string `json:"nickname"`
Port int32 `json:"port"`
IsSSL bool `json:"is-ssl"`
IsLoginAuth bool `json:"is-loginauth"`
} `json:"email"`
} `json:"config"`
Config json.RawMessage `json:"config"`
}

View File

@ -1,6 +1,9 @@
package dto
import "encoding/json"
import (
"encoding/json"
"time"
)
type TaskRequest struct {
ID uint `json:"ID"`
@ -26,3 +29,44 @@ type ToggleTaskRequest struct {
ID uint `json:"ID"`
Enabled bool `json:"enabled"`
}
type TaskResponse struct {
ID uint `json:"ID"`
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
DeletedAt any `json:"DeletedAt"`
Name string `json:"name"`
Description string `json:"description"`
Spec string `json:"spec"`
WithSeconds bool `json:"withSeconds"`
ExecutorType string `json:"executorType"`
MethodName string `json:"methodName"`
Params json.RawMessage `json:"params"`
HTTPURL string `json:"httpUrl"`
HTTPMethod string `json:"httpMethod"`
HTTPHeader json.RawMessage `json:"httpHeader"`
HTTPBody string `json:"httpBody"`
HTTPAllowPrivate bool `json:"httpAllowPrivate"`
Enabled bool `json:"enabled"`
NextRunAt *time.Time `json:"nextRunAt"`
}
type TaskLogResponse struct {
ID uint `json:"ID"`
CreatedAt time.Time `json:"CreatedAt"`
DeletedAt any `json:"DeletedAt"`
TaskID uint `json:"taskId"`
TaskName string `json:"taskName"`
TriggerType string `json:"triggerType"`
StartedAt time.Time `json:"startedAt"`
FinishedAt time.Time `json:"finishedAt"`
DurationMS int64 `json:"durationMs"`
Status string `json:"status"`
ErrorMsg string `json:"errorMsg"`
Output string `json:"output"`
}
type TaskMethodResponse struct {
Name string `json:"name"`
Description string `json:"description"`
}

View File

@ -1,5 +1,18 @@
package dto
import "time"
type VersionResponse struct {
ID uint `json:"ID"`
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
DeletedAt any `json:"DeletedAt"`
VersionName string `json:"versionName"`
VersionCode string `json:"versionCode"`
Description string `json:"description"`
VersionData string `json:"versionData"`
}
type VersionMenuMetaRequest struct {
ActiveName string `json:"activeName"`
KeepAlive bool `json:"keepAlive"`

View File

@ -83,19 +83,19 @@ func (s *ExportService) CreateRequest(ctx context.Context, req *dto.ExportTempla
func (s *ExportService) UpdateRequest(ctx context.Context, req *dto.ExportTemplateRequest) error {
return s.Update(ctx, exportTemplateDomain(req))
}
func (s *ExportService) TemplatesFilter(ctx context.Context, page, size int, name, tableName, templateID string, start, end *time.Time) ([]map[string]any, int64, error) {
func (s *ExportService) TemplatesFilter(ctx context.Context, page, size int, name, tableName, templateID string, start, end *time.Time) ([]*dto.ExportTemplateResponse, int64, error) {
return s.Templates(ctx, page, size, &biz.ExportTemplate{Name: name, TableName: tableName, TemplateID: templateID, StartCreatedAt: start, EndCreatedAt: end})
}
func exportDTO(v *biz.ExportTemplate) map[string]any {
conditions := make([]map[string]any, 0, len(v.Conditions))
func exportDTO(v *biz.ExportTemplate) *dto.ExportTemplateResponse {
conditions := make([]dto.ExportConditionResponse, 0, len(v.Conditions))
for _, x := range v.Conditions {
conditions = append(conditions, map[string]any{"from": x.From, "column": x.Column, "operator": x.Operator})
conditions = append(conditions, dto.ExportConditionResponse{From: x.From, Column: x.Column, Operator: x.Operator})
}
joins := make([]map[string]any, 0, len(v.Joins))
joins := make([]dto.ExportJoinResponse, 0, len(v.Joins))
for _, x := range v.Joins {
joins = append(joins, map[string]any{"joins": x.Join, "table": x.Table, "on": x.On})
joins = append(joins, dto.ExportJoinResponse{Join: x.Join, Table: x.Table, On: x.On})
}
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "dbName": v.DBName, "name": v.Name, "tableName": v.TableName, "templateID": v.TemplateID, "templateInfo": v.TemplateInfo, "sql": v.SQL, "importSql": v.ImportSQL, "limit": v.Limit, "order": v.Order, "conditions": conditions, "joinTemplate": joins}
return &dto.ExportTemplateResponse{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, DeletedAt: nil, DBName: v.DBName, Name: v.Name, TableName: v.TableName, TemplateID: v.TemplateID, TemplateInfo: v.TemplateInfo, SQL: v.SQL, ImportSQL: v.ImportSQL, Limit: v.Limit, Order: v.Order, Conditions: conditions, Joins: joins}
}
func (s *ExportService) Create(ctx context.Context, v *biz.ExportTemplate) error {
return s.uc.CreateExportTemplate(ctx, v)
@ -106,19 +106,19 @@ func (s *ExportService) Update(ctx context.Context, v *biz.ExportTemplate) error
func (s *ExportService) Delete(ctx context.Context, ids []uint) error {
return s.uc.DeleteExportTemplates(ctx, ids)
}
func (s *ExportService) Template(ctx context.Context, id uint, tid string) (map[string]any, error) {
func (s *ExportService) Template(ctx context.Context, id uint, tid string) (*dto.ExportTemplateResponse, error) {
v, err := s.uc.FindExportTemplate(ctx, id, tid)
if err != nil {
return nil, err
}
return exportDTO(v), nil
}
func (s *ExportService) Templates(ctx context.Context, page, size int, q *biz.ExportTemplate) ([]map[string]any, int64, error) {
func (s *ExportService) Templates(ctx context.Context, page, size int, q *biz.ExportTemplate) ([]*dto.ExportTemplateResponse, int64, error) {
items, total, err := s.uc.ListExportTemplates(ctx, page, size, q)
if err != nil {
return nil, 0, err
}
out := make([]map[string]any, 0, len(items))
out := make([]*dto.ExportTemplateResponse, 0, len(items))
for _, v := range items {
out = append(out, exportDTO(v))
}

View File

@ -19,36 +19,36 @@ func NewMediaService(uc *biz.MediaUsecase, settings biz.RuntimeSettings) *MediaS
func (s *MediaService) MediaConfig() biz.MediaSettings {
return s.settings.MediaSettings()
}
func mediaDTO(v *biz.MediaFile) map[string]any {
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "name": v.Name, "classId": v.CategoryID, "url": v.URL, "tag": v.Tag, "key": v.Key, "size": v.Size, "mime": v.Mime, "md5": v.MD5, "userId": v.UserID}
func mediaDTO(v *biz.MediaFile) *dto.MediaResponse {
return &dto.MediaResponse{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, DeletedAt: nil, Name: v.Name, ClassID: v.CategoryID, URL: v.URL, Tag: v.Tag, Key: v.Key, Size: v.Size, Mime: v.Mime, MD5: v.MD5, UserID: v.UserID}
}
func categoryDTO(v *biz.AttachmentCategory) map[string]any {
children := make([]map[string]any, 0, len(v.Children))
func categoryDTO(v *biz.AttachmentCategory) *dto.MediaCategoryResponse {
children := make([]*dto.MediaCategoryResponse, 0, len(v.Children))
for _, x := range v.Children {
children = append(children, categoryDTO(x))
}
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "name": v.Name, "pid": v.ParentID, "children": children}
return &dto.MediaCategoryResponse{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, DeletedAt: nil, Name: v.Name, PID: v.ParentID, Children: children}
}
func (s *MediaService) Upload(ctx context.Context, userID uint, name, mime string, categoryID int, reader io.Reader, save bool) (map[string]any, error) {
func (s *MediaService) Upload(ctx context.Context, userID uint, name, mime string, categoryID int, reader io.Reader, save bool) (*dto.MediaResponse, error) {
v, err := s.uc.Upload(ctx, userID, name, mime, categoryID, reader, save)
if err != nil {
return nil, err
}
return mediaDTO(v), nil
}
func (s *MediaService) Media(ctx context.Context, id uint) (map[string]any, error) {
func (s *MediaService) Media(ctx context.Context, id uint) (*dto.MediaResponse, error) {
v, err := s.uc.FindMedia(ctx, id)
if err != nil {
return nil, err
}
return mediaDTO(v), nil
}
func (s *MediaService) MediaList(ctx context.Context, filter biz.MediaFilter) ([]map[string]any, int64, error) {
func (s *MediaService) MediaList(ctx context.Context, filter biz.MediaFilter) ([]*dto.MediaResponse, int64, error) {
items, total, err := s.uc.ListMedia(ctx, filter)
if err != nil {
return nil, 0, err
}
out := make([]map[string]any, 0, len(items))
out := make([]*dto.MediaResponse, 0, len(items))
for _, v := range items {
out = append(out, mediaDTO(v))
}
@ -68,23 +68,23 @@ func (s *MediaService) ImportURLRequests(ctx context.Context, values []dto.Impor
}
return s.ImportURLs(ctx, items)
}
func (s *MediaService) Storage(ctx context.Context, prefix, cursor string, limit int) ([]map[string]any, string, bool, error) {
func (s *MediaService) Storage(ctx context.Context, prefix, cursor string, limit int) ([]*dto.StorageObjectResponse, string, bool, error) {
items, next, more, err := s.uc.ListStorage(ctx, prefix, cursor, limit)
if err != nil {
return nil, "", false, err
}
out := make([]map[string]any, 0, len(items))
out := make([]*dto.StorageObjectResponse, 0, len(items))
for _, v := range items {
out = append(out, map[string]any{"name": v.Name, "key": v.Path, "url": v.URL, "size": v.Size})
out = append(out, &dto.StorageObjectResponse{Name: v.Name, Key: v.Path, URL: v.URL, Size: v.Size})
}
return out, next, more, nil
}
func (s *MediaService) Categories(ctx context.Context) ([]map[string]any, error) {
func (s *MediaService) Categories(ctx context.Context) ([]*dto.MediaCategoryResponse, error) {
items, err := s.uc.ListCategories(ctx)
if err != nil {
return nil, err
}
out := make([]map[string]any, 0, len(items))
out := make([]*dto.MediaCategoryResponse, 0, len(items))
for _, v := range items {
out = append(out, categoryDTO(v))
}

View File

@ -3,22 +3,24 @@ package service
import (
"context"
"io"
"kra/internal/service/dto"
)
func (s *MediaService) InitUpload(ctx context.Context, userID uint, name, hash string, size, chunkSize int64, total int) (map[string]any, error) {
func (s *MediaService) InitUpload(ctx context.Context, userID uint, name, hash string, size, chunkSize int64, total int) (*dto.InitUploadResponse, error) {
session, media, chunks, err := s.uc.InitUpload(ctx, userID, name, hash, size, chunkSize, total)
if err != nil {
return nil, err
}
if media != nil {
return map[string]any{"instant": true, "media": mediaDTO(media), "uploadedChunks": []int{}}, nil
return &dto.InitUploadResponse{Instant: true, Media: mediaDTO(media), UploadedChunks: []int{}}, nil
}
return map[string]any{"instant": false, "uploadId": session.ID, "uploadedChunks": chunks, "media": nil}, nil
return &dto.InitUploadResponse{Instant: false, UploadID: session.ID, UploadedChunks: chunks, Media: nil}, nil
}
func (s *MediaService) SaveChunk(ctx context.Context, userID, uploadID uint, index int, hash string, reader io.Reader) error {
return s.uc.SaveChunk(ctx, userID, uploadID, index, hash, reader)
}
func (s *MediaService) CompleteUpload(ctx context.Context, userID, uploadID uint) (map[string]any, error) {
func (s *MediaService) CompleteUpload(ctx context.Context, userID, uploadID uint) (*dto.MediaResponse, error) {
v, err := s.uc.CompleteUpload(ctx, userID, uploadID, "")
if err != nil {
return nil, err

View File

@ -23,24 +23,24 @@ func (s *ParameterService) CreateParameterRequest(ctx context.Context, req *dto.
func (s *ParameterService) UpdateParameterRequest(ctx context.Context, req *dto.SystemParameterRequest) error {
return s.UpdateParameter(ctx, parameterDomain(req))
}
func (s *ParameterService) ParametersFilter(ctx context.Context, page, size int, name, key string, start, end *time.Time) ([]map[string]any, int64, error) {
func (s *ParameterService) ParametersFilter(ctx context.Context, page, size int, name, key string, start, end *time.Time) ([]*dto.SystemParameterResponse, int64, error) {
return s.Parameters(ctx, page, size, &biz.SystemParameter{Name: name, Key: key, StartCreatedAt: start, EndCreatedAt: end})
}
func parameterDTO(v *biz.SystemParameter) map[string]any {
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "name": v.Name, "key": v.Key, "value": v.Value, "desc": v.Desc}
func parameterDTO(v *biz.SystemParameter) *dto.SystemParameterResponse {
return &dto.SystemParameterResponse{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, DeletedAt: nil, Name: v.Name, Key: v.Key, Value: v.Value, Desc: v.Desc}
}
func (s *ParameterService) Parameters(ctx context.Context, page, size int, q *biz.SystemParameter) ([]map[string]any, int64, error) {
func (s *ParameterService) Parameters(ctx context.Context, page, size int, q *biz.SystemParameter) ([]*dto.SystemParameterResponse, int64, error) {
items, total, err := s.uc.ListParameters(ctx, page, size, q)
if err != nil {
return nil, 0, err
}
out := make([]map[string]any, 0, len(items))
out := make([]*dto.SystemParameterResponse, 0, len(items))
for _, v := range items {
out = append(out, parameterDTO(v))
}
return out, total, nil
}
func (s *ParameterService) Parameter(ctx context.Context, id uint, key string) (map[string]any, error) {
func (s *ParameterService) Parameter(ctx context.Context, id uint, key string) (*dto.SystemParameterResponse, error) {
v, err := s.uc.FindParameter(ctx, id, key)
if err != nil {
return nil, err

View File

@ -0,0 +1,25 @@
package service
import (
"context"
"kra/internal/biz"
)
type PermissionService struct{ uc *biz.PermissionUsecase }
func NewPermissionService(uc *biz.PermissionUsecase) *PermissionService {
return &PermissionService{uc: uc}
}
func (s *PermissionService) SelectedButtons(ctx context.Context, authorityID, menuID uint) ([]uint, error) {
return s.uc.SelectedButtons(ctx, authorityID, menuID)
}
func (s *PermissionService) SetSelectedButtons(ctx context.Context, authorityID, menuID uint, ids []uint) error {
return s.uc.SetSelectedButtons(ctx, authorityID, menuID, ids)
}
func (s *PermissionService) CanRemoveButton(ctx context.Context, id uint) (bool, error) {
return s.uc.CanRemoveButton(ctx, id)
}

View File

@ -7,26 +7,26 @@ import (
"kra/internal/service/dto"
)
func securityDTO(v *biz.SecurityConfig) map[string]any {
return map[string]any{"ID": v.ID, "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 securityDTO(v *biz.SecurityConfig) *dto.SecurityConfigResponse {
return &dto.SecurityConfigResponse{ID: v.ID, 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 (s *SecurityService) CurrentSecurity(ctx context.Context) (*biz.SecurityConfig, error) {
return s.uc.Current(ctx)
}
func (s *SecurityService) Security(ctx context.Context) (map[string]any, error) {
func (s *SecurityService) Security(ctx context.Context) (*dto.SecurityConfigResponse, error) {
value, err := s.CurrentSecurity(ctx)
if err != nil {
return nil, err
}
return securityDTO(value), nil
}
func (s *SecurityService) SaveSecurity(ctx context.Context, value *biz.SecurityConfig) (map[string]any, error) {
func (s *SecurityService) SaveSecurity(ctx context.Context, value *biz.SecurityConfig) (*dto.SecurityConfigResponse, error) {
if err := s.uc.UpdateSecurity(ctx, value); err != nil {
return nil, err
}
return securityDTO(value), nil
}
func (s *SecurityService) SaveSecurityRequest(ctx context.Context, value *dto.SecurityConfigRequest) (map[string]any, error) {
func (s *SecurityService) SaveSecurityRequest(ctx context.Context, value *dto.SecurityConfigRequest) (*dto.SecurityConfigResponse, error) {
return s.SaveSecurity(ctx, &biz.SecurityConfig{
ID: value.ID, CaptchaOpen: value.CaptchaOpen, CaptchaTimeout: value.CaptchaTimeout,
KeyLong: value.KeyLong, ImgWidth: value.ImgWidth, ImgHeight: value.ImgHeight,

View File

@ -4,16 +4,15 @@ import (
"context"
"kra/internal/biz"
"kra/internal/conf"
)
type SystemConfigService struct {
uc *biz.SystemConfigUsecase
runtime *conf.Runtime
uc *biz.SystemConfigUsecase
settings biz.RuntimeSettings
}
func NewSystemConfigService(uc *biz.SystemConfigUsecase, runtime *conf.Runtime) *SystemConfigService {
return &SystemConfigService{uc: uc, runtime: runtime}
func NewSystemConfigService(uc *biz.SystemConfigUsecase, settings biz.RuntimeSettings) *SystemConfigService {
return &SystemConfigService{uc: uc, settings: settings}
}
func (s *SystemConfigService) IsInitialized(ctx context.Context) (bool, error) {

View File

@ -4,125 +4,23 @@ import (
"context"
"encoding/json"
"kra/internal/conf"
"kra/internal/service/dto"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
)
func (s *SystemConfigService) PersistConfig(ctx context.Context) error {
return s.uc.PersistConfig(ctx)
}
func (s *SystemConfigService) PersistAdminConfig(ctx context.Context, value *conf.AdminBackend) error {
raw, err := protojson.MarshalOptions{UseProtoNames: true}.Marshal(value)
if err != nil {
return err
}
return s.uc.PersistAdminConfig(ctx, raw)
func (s *SystemConfigService) ReloadConfig(ctx context.Context) error {
return s.uc.ReloadConfig(ctx)
}
func (s *SystemConfigService) ReloadConfig(ctx context.Context) error { return s.uc.ReloadConfig(ctx) }
func (s *SystemConfigService) DiskMountPoints() []string { return s.uc.DiskMountPoints() }
func (s *SystemConfigService) DiskMountPoints() []string {
config := s.runtime.Admin()
if config == nil {
return nil
}
points := make([]string, 0, len(config.DiskList))
for _, item := range config.DiskList {
if item != nil && item.MountPoint != "" {
points = append(points, item.MountPoint)
}
}
return points
}
func (s *SystemConfigService) SystemConfig() map[string]any {
admin := map[string]any{"routerPrefix": ""}
email := map[string]any{}
config := s.runtime.Admin()
if config == nil {
return map[string]any{"config": map[string]any{"admin": admin, "email": email, "data": map[string]any{}}}
}
admin["routerPrefix"] = config.RouterPrefix
if config.System != nil {
admin["system"] = map[string]any{"useRedis": config.System.UseRedis, "useMultipoint": config.System.UseMultipoint, "useStrictAuth": config.System.UseStrictAuth, "disableAutoMigrate": config.System.DisableAutoMigrate, "useMongo": config.System.UseMongo}
}
if config.Jwt != nil {
admin["jwt"] = map[string]any{"signingKey": "******", "expiresTime": config.Jwt.ExpiresTime.AsDuration().String(), "bufferTime": config.Jwt.BufferTime.AsDuration().String(), "issuer": config.Jwt.Issuer}
}
if config.Captcha != nil {
admin["captcha"] = map[string]any{"keyLong": config.Captcha.KeyLong, "imgWidth": config.Captcha.ImgWidth, "imgHeight": config.Captcha.ImgHeight, "storeExpiration": config.Captcha.StoreExpiration.AsDuration().String()}
}
if config.Local != nil {
admin["local"] = map[string]any{"storePath": config.Local.StorePath, "pathPrefix": config.Local.PathPrefix}
}
if config.Media != nil {
admin["media"] = map[string]any{"sessionTtl": config.Media.SessionTtl, "maxFileSize": config.Media.MaxFileSize}
}
if config.Email != nil {
email = map[string]any{"to": config.Email.To, "from": config.Email.From, "host": config.Email.Host, "secret": "******", "nickname": config.Email.Nickname, "port": config.Email.Port, "is-ssl": config.Email.IsSsl, "is-loginauth": config.Email.IsLoginAuth}
}
if config.Storage != nil {
storage := proto.Clone(config.Storage).(*conf.AdminBackend_Storage)
maskStorageSecrets(storage)
admin["storage"] = storage
}
if config.Zap != nil {
admin["zap"] = config.Zap
}
if config.Cors != nil {
admin["cors"] = config.Cors
}
if config.App != nil {
admin["app"] = config.App
}
data := s.runtime.Data()
if data != nil {
if data.Database != nil {
data.Database.Password = "******"
}
if data.Redis != nil {
data.Redis.Password = "******"
}
if data.Mongo != nil {
data.Mongo.Password = "******"
}
for _, database := range data.DatabaseList {
if database != nil {
database.Password = "******"
}
}
for _, redis := range data.RedisList {
if redis != nil {
redis.Password = "******"
}
}
}
dataMap := map[string]any{}
if raw, err := (protojson.MarshalOptions{UseProtoNames: true}).Marshal(data); err == nil {
_ = json.Unmarshal(raw, &dataMap)
}
return map[string]any{"config": map[string]any{"admin": admin, "email": email, "data": dataMap}}
func (s *SystemConfigService) SystemConfig() (json.RawMessage, error) {
return s.uc.ConfigurationJSON()
}
func (s *SystemConfigService) SaveSystemConfig(ctx context.Context, req *dto.SetSystemConfigRequest) error {
config := s.runtime.Admin()
if config == nil {
return nil
}
next := proto.Clone(config).(*conf.AdminBackend)
applyAdminConfig(next, req)
data := applyDataConfig(s.runtime.Data(), req)
dataRaw, err := protojson.MarshalOptions{UseProtoNames: true}.Marshal(data)
if err != nil {
return err
}
adminRaw, err := protojson.MarshalOptions{UseProtoNames: true}.Marshal(next)
if err != nil {
return err
}
return s.uc.PersistRuntimeConfig(ctx, dataRaw, adminRaw)
return s.uc.SaveConfigurationJSON(ctx, req.Config)
}

View File

@ -1,197 +0,0 @@
package service
import (
"time"
"kra/internal/conf"
"kra/internal/service/dto"
"google.golang.org/protobuf/types/known/durationpb"
)
func applyAdminConfig(next *conf.AdminBackend, req *dto.SetSystemConfigRequest) {
next.RouterPrefix = req.Config.Admin.RouterPrefix
if next.System == nil {
next.System = &conf.AdminBackend_System{}
}
next.System.UseRedis = req.Config.Admin.System.UseRedis
next.System.UseMultipoint = req.Config.Admin.System.UseMultipoint
next.System.UseStrictAuth = req.Config.Admin.System.UseStrictAuth
next.System.DisableAutoMigrate = req.Config.Admin.System.DisableAutoMigrate
next.System.UseMongo = req.Config.Admin.System.UseMongo
if req.Config.Admin.Zap != nil {
next.Zap = req.Config.Admin.Zap
}
if req.Config.Admin.Cors != nil {
next.Cors = req.Config.Admin.Cors
}
if req.Config.Admin.App != nil {
next.App = req.Config.Admin.App
}
applyJWTConfig(next, req)
applyCaptchaConfig(next, req)
applyMediaConfig(next, req)
if req.Config.Admin.Storage != nil {
preserveStorageSecrets(req.Config.Admin.Storage, next.Storage)
next.Storage = req.Config.Admin.Storage
}
if next.Email != nil && req.Config.Email != nil {
next.Email.To, next.Email.From, next.Email.Host = req.Config.Email.To, req.Config.Email.From, req.Config.Email.Host
next.Email.Nickname, next.Email.Port = req.Config.Email.Nickname, req.Config.Email.Port
next.Email.IsSsl, next.Email.IsLoginAuth = req.Config.Email.IsSSL, req.Config.Email.IsLoginAuth
if req.Config.Email.Secret != "" && req.Config.Email.Secret != "******" {
next.Email.Secret = req.Config.Email.Secret
}
}
}
func applyJWTConfig(next *conf.AdminBackend, req *dto.SetSystemConfigRequest) {
if next.Jwt == nil {
return
}
if req.Config.Admin.JWT.SigningKey != "" && req.Config.Admin.JWT.SigningKey != "******" {
next.Jwt.SigningKey = req.Config.Admin.JWT.SigningKey
}
if req.Config.Admin.JWT.Issuer != "" {
next.Jwt.Issuer = req.Config.Admin.JWT.Issuer
}
if value, err := time.ParseDuration(req.Config.Admin.JWT.ExpiresTime); err == nil && value > 0 {
next.Jwt.ExpiresTime = durationpb.New(value)
}
if value, err := time.ParseDuration(req.Config.Admin.JWT.BufferTime); err == nil && value >= 0 {
next.Jwt.BufferTime = durationpb.New(value)
}
}
func applyCaptchaConfig(next *conf.AdminBackend, req *dto.SetSystemConfigRequest) {
if next.Captcha != nil {
if req.Config.Admin.Captcha.KeyLong > 0 {
next.Captcha.KeyLong = req.Config.Admin.Captcha.KeyLong
}
if req.Config.Admin.Captcha.ImgWidth > 0 {
next.Captcha.ImgWidth = req.Config.Admin.Captcha.ImgWidth
}
if req.Config.Admin.Captcha.ImgHeight > 0 {
next.Captcha.ImgHeight = req.Config.Admin.Captcha.ImgHeight
}
if value, err := time.ParseDuration(req.Config.Admin.Captcha.StoreExpiration); err == nil && value > 0 {
next.Captcha.StoreExpiration = durationpb.New(value)
}
}
if next.Local != nil {
if req.Config.Admin.Local.StorePath != "" {
next.Local.StorePath = req.Config.Admin.Local.StorePath
}
if req.Config.Admin.Local.PathPrefix != "" {
next.Local.PathPrefix = req.Config.Admin.Local.PathPrefix
}
}
}
func applyMediaConfig(next *conf.AdminBackend, req *dto.SetSystemConfigRequest) {
if next.Media == nil {
return
}
if req.Config.Admin.Media.SessionTTL > 0 {
next.Media.SessionTtl = req.Config.Admin.Media.SessionTTL
}
next.Media.MaxFileSize = req.Config.Admin.Media.MaxFileSize
}
func applyDataConfig(data *conf.Data, req *dto.SetSystemConfigRequest) *conf.Data {
if req.Config.Data == nil {
return data
}
if data == nil {
data = &conf.Data{}
}
applyDatabaseConfig(data, req)
applyRedisConfig(data, req)
if req.Config.Data.DatabaseList != nil {
for i, item := range req.Config.Data.DatabaseList {
if item != nil && (item.Password == "" || item.Password == "******") && i < len(data.DatabaseList) && data.DatabaseList[i] != nil {
item.Password = data.DatabaseList[i].Password
}
}
data.DatabaseList = req.Config.Data.DatabaseList
}
if req.Config.Data.RedisList != nil {
for i, item := range req.Config.Data.RedisList {
if item != nil && (item.Password == "" || item.Password == "******") && i < len(data.RedisList) && data.RedisList[i] != nil {
item.Password = data.RedisList[i].Password
}
}
data.RedisList = req.Config.Data.RedisList
}
if req.Config.Data.Mongo != nil {
if data.Mongo != nil && (req.Config.Data.Mongo.Password == "" || req.Config.Data.Mongo.Password == "******") {
req.Config.Data.Mongo.Password = data.Mongo.Password
}
data.Mongo = req.Config.Data.Mongo
}
return data
}
func applyDatabaseConfig(data *conf.Data, req *dto.SetSystemConfigRequest) {
value := req.Config.Data.Database
if value == nil {
return
}
password := value.Password
if data.Database != nil && (password == "" || password == "******") {
password = data.Database.Password
}
data.Database = &conf.Data_Database{Driver: value.Driver, Source: value.Source, Host: value.Host, Port: value.Port, User: value.User, Password: password, Name: value.Name, Config: value.Config, Path: value.Path, Prefix: value.Prefix, Engine: value.Engine, LogMode: value.LogMode, MaxIdleConns: value.MaxIdleConns, MaxOpenConns: value.MaxOpenConns, ConnMaxLifetime: value.ConnMaxLifetime, Singular: value.Singular}
}
func applyRedisConfig(data *conf.Data, req *dto.SetSystemConfigRequest) {
value := req.Config.Data.Redis
if value == nil {
return
}
password := value.Password
if data.Redis != nil && (password == "" || password == "******") {
password = data.Redis.Password
}
redis := &conf.Data_Redis{Network: value.Network, Addr: value.Addr, Name: value.Name, Password: password, Db: value.DB, UseCluster: value.UseCluster, ClusterAddrs: value.ClusterAddrs}
if duration, err := time.ParseDuration(value.ReadTimeout); err == nil && duration >= 0 {
redis.ReadTimeout = durationpb.New(duration)
}
if duration, err := time.ParseDuration(value.WriteTimeout); err == nil && duration >= 0 {
redis.WriteTimeout = durationpb.New(duration)
}
data.Redis = redis
}
func objectStores(storage *conf.AdminBackend_Storage) []*conf.AdminBackend_ObjectStore {
if storage == nil {
return nil
}
return []*conf.AdminBackend_ObjectStore{storage.AliyunOss, storage.HuaweiObs, storage.TencentCos, storage.AwsS3, storage.CloudflareR2, storage.Minio}
}
func maskStorageSecrets(storage *conf.AdminBackend_Storage) {
if storage.Qiniu != nil {
storage.Qiniu.SecretKey = "******"
}
for _, item := range objectStores(storage) {
if item != nil {
item.SecretKey = "******"
}
}
}
func preserveStorageSecrets(next, current *conf.AdminBackend_Storage) {
if next == nil || current == nil {
return
}
if next.Qiniu != nil && current.Qiniu != nil && (next.Qiniu.SecretKey == "" || next.Qiniu.SecretKey == "******") {
next.Qiniu.SecretKey = current.Qiniu.SecretKey
}
nextItems, currentItems := objectStores(next), objectStores(current)
for i := range nextItems {
if nextItems[i] != nil && currentItems[i] != nil && (nextItems[i].SecretKey == "" || nextItems[i].SecretKey == "******") {
nextItems[i].SecretKey = currentItems[i].SecretKey
}
}
}

View File

@ -4,12 +4,14 @@ import (
"runtime"
"time"
"kra/internal/service/dto"
"github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/disk"
"github.com/shirou/gopsutil/v4/mem"
)
func (s *SystemConfigService) ServerInfo() (map[string]any, error) {
func (s *SystemConfigService) ServerInfo() (*dto.ServerInfoResponse, error) {
physicalCores, err := cpu.Counts(false)
if err != nil {
return nil, err
@ -22,19 +24,19 @@ func (s *SystemConfigService) ServerInfo() (map[string]any, error) {
if err != nil {
return nil, err
}
diskInfo := make([]map[string]any, 0)
diskInfo := make([]dto.ServerDiskResponse, 0)
for _, mountPoint := range s.DiskMountPoints() {
usage, usageErr := disk.Usage(mountPoint)
if usageErr != nil {
return nil, usageErr
}
diskInfo = append(diskInfo, map[string]any{"mountPoint": mountPoint, "usedMb": usage.Used / 1024 / 1024, "usedGb": usage.Used / 1024 / 1024 / 1024, "totalMb": usage.Total / 1024 / 1024, "totalGb": usage.Total / 1024 / 1024 / 1024, "usedPercent": int(usage.UsedPercent)})
diskInfo = append(diskInfo, dto.ServerDiskResponse{MountPoint: mountPoint, UsedMB: usage.Used / 1024 / 1024, UsedGB: usage.Used / 1024 / 1024 / 1024, TotalMB: usage.Total / 1024 / 1024, TotalGB: usage.Total / 1024 / 1024 / 1024, UsedPercent: int(usage.UsedPercent)})
}
usedMB, totalMB := memory.Used/1024/1024, memory.Total/1024/1024
return map[string]any{
"os": map[string]any{"goos": runtime.GOOS, "numCpu": runtime.NumCPU(), "compiler": runtime.Compiler, "goVersion": runtime.Version(), "numGoroutine": runtime.NumGoroutine()},
"cpu": map[string]any{"cpus": cpuPercent, "cores": physicalCores},
"ram": map[string]any{"usedMb": usedMB, "totalMb": totalMB, "usedPercent": int(memory.UsedPercent)},
"disk": diskInfo,
return &dto.ServerInfoResponse{
OS: dto.ServerOSResponse{GOOS: runtime.GOOS, NumCPU: runtime.NumCPU(), Compiler: runtime.Compiler, GoVersion: runtime.Version(), NumGoroutine: runtime.NumGoroutine()},
CPU: dto.ServerCPUResponse{CPUs: cpuPercent, Cores: physicalCores},
RAM: dto.ServerRAMResponse{UsedMB: usedMB, TotalMB: totalMB, UsedPercent: int(memory.UsedPercent)},
Disk: diskInfo,
}, nil
}

View File

@ -35,8 +35,8 @@ func (s *SystemConfigService) InitializeRoutes(ctx context.Context, input *Datab
apis := make([]*biz.API, 0, len(routes))
for _, route := range routes {
path := route.Path
if config := s.runtime.Admin(); config != nil && config.RouterPrefix != "" {
path = strings.TrimPrefix(path, strings.TrimSuffix(config.RouterPrefix, "/"))
if routerPrefix := s.settings.RouterPrefix(); routerPrefix != "" {
path = strings.TrimPrefix(path, strings.TrimSuffix(routerPrefix, "/"))
if path == "" {
path = "/"
}

View File

@ -29,7 +29,7 @@ func (s *TaskService) CreateRequest(ctx context.Context, req *dto.TaskRequest) (
func (s *TaskService) UpdateRequest(ctx context.Context, req *dto.TaskRequest) error {
return s.Update(ctx, taskDomain(req))
}
func (s *TaskService) ListRequest(ctx context.Context, page, size int, name, executorType string, enabled *bool) ([]map[string]any, int64, error) {
func (s *TaskService) ListRequest(ctx context.Context, page, size int, name, executorType string, enabled *bool) ([]*dto.TaskResponse, int64, error) {
return s.Tasks(ctx, page, size, &biz.TimedTask{Name: name, ExecutorType: executorType, EnabledFilter: enabled})
}
func (s *TaskService) Create(ctx context.Context, v *biz.TimedTask) error {
@ -44,15 +44,15 @@ func (s *TaskService) Delete(ctx context.Context, id uint) error {
func (s *TaskService) Toggle(ctx context.Context, id uint, enabled bool) error {
return s.uc.Toggle(ctx, id, enabled)
}
func taskDTO(v *biz.TimedTask, next *time.Time) map[string]any {
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "name": v.Name, "description": v.Description, "spec": v.Spec, "withSeconds": v.WithSeconds, "executorType": v.ExecutorType, "methodName": v.MethodName, "params": json.RawMessage(v.Params), "httpUrl": v.HTTPURL, "httpMethod": v.HTTPMethod, "httpHeader": json.RawMessage(v.HTTPHeader), "httpBody": v.HTTPBody, "httpAllowPrivate": v.HTTPAllowPrivate, "enabled": v.Enabled, "nextRunAt": next}
func taskDTO(v *biz.TimedTask, next *time.Time) *dto.TaskResponse {
return &dto.TaskResponse{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, DeletedAt: nil, Name: v.Name, Description: v.Description, Spec: v.Spec, WithSeconds: v.WithSeconds, ExecutorType: v.ExecutorType, MethodName: v.MethodName, Params: json.RawMessage(v.Params), HTTPURL: v.HTTPURL, HTTPMethod: v.HTTPMethod, HTTPHeader: json.RawMessage(v.HTTPHeader), HTTPBody: v.HTTPBody, HTTPAllowPrivate: v.HTTPAllowPrivate, Enabled: v.Enabled, NextRunAt: next}
}
func (s *TaskService) Tasks(ctx context.Context, page, size int, q *biz.TimedTask) ([]map[string]any, int64, error) {
func (s *TaskService) Tasks(ctx context.Context, page, size int, q *biz.TimedTask) ([]*dto.TaskResponse, int64, error) {
items, total, next, err := s.uc.List(ctx, page, size, q)
if err != nil {
return nil, 0, err
}
out := make([]map[string]any, 0, len(items))
out := make([]*dto.TaskResponse, 0, len(items))
for _, v := range items {
var ptr *time.Time
if value, ok := next[v.ID]; ok {
@ -64,14 +64,14 @@ func (s *TaskService) Tasks(ctx context.Context, page, size int, q *biz.TimedTas
return out, total, nil
}
func (s *TaskService) Logs(ctx context.Context, page, size int, taskID uint, status string) ([]map[string]any, int64, error) {
func (s *TaskService) Logs(ctx context.Context, page, size int, taskID uint, status string) ([]*dto.TaskLogResponse, int64, error) {
items, total, err := s.uc.Logs(ctx, page, size, taskID, status)
if err != nil {
return nil, 0, err
}
out := make([]map[string]any, 0, len(items))
out := make([]*dto.TaskLogResponse, 0, len(items))
for _, v := range items {
out = append(out, map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "DeletedAt": nil, "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})
out = append(out, &dto.TaskLogResponse{ID: v.ID, CreatedAt: v.CreatedAt, DeletedAt: nil, TaskID: v.TaskID, TaskName: v.TaskName, TriggerType: v.TriggerType, StartedAt: v.StartedAt, FinishedAt: v.FinishedAt, DurationMS: v.DurationMS, Status: v.Status, ErrorMsg: v.ErrorMsg, Output: v.Output})
}
return out, total, nil
}
@ -81,11 +81,11 @@ func (s *TaskService) Reload(ctx context.Context) error { return s.uc.
func (s *TaskService) Subscribe() chan []byte { return s.uc.Subscribe() }
func (s *TaskService) Unsubscribe(events chan []byte) { s.uc.Unsubscribe(events) }
func (s *TaskService) RegisteredMethods() []map[string]any {
func (s *TaskService) RegisteredMethods() []*dto.TaskMethodResponse {
methods := biz.RegisteredTaskMethods()
out := make([]map[string]any, 0, len(methods))
out := make([]*dto.TaskMethodResponse, 0, len(methods))
for _, method := range methods {
out = append(out, map[string]any{"name": method.Name, "description": method.Description})
out = append(out, &dto.TaskMethodResponse{Name: method.Name, Description: method.Description})
}
return out
}

View File

@ -27,17 +27,17 @@ func NewUserService(uc *biz.UserUsecase, settings *SecurityService) *UserService
func userInput(value *dto.UserRequest) UserInput {
return UserInput{ID: value.ID, Username: value.Username, Password: value.Password, NickName: value.NickName, HeaderImg: value.HeaderImg, AuthorityID: value.AuthorityID, AuthorityIDs: value.AuthorityIDs, Enable: value.Enable, Phone: value.Phone, Email: value.Email}
}
func (s *UserService) ListUsersRequest(ctx context.Context, value *dto.UserListRequest) ([]map[string]any, int64, error) {
func (s *UserService) ListUsersRequest(ctx context.Context, value *dto.UserListRequest) ([]*dto.UserResponse, int64, error) {
return s.ListUsers(ctx, value.Page, value.PageSize, &biz.UserListFilter{Username: value.Username, NickName: value.NickName, Phone: value.Phone, Email: value.Email, OrderKey: value.OrderKey, Desc: value.Desc})
}
func (s *UserService) CreateUserRequest(ctx context.Context, value *dto.UserRequest) (map[string]any, error) {
func (s *UserService) CreateUserRequest(ctx context.Context, value *dto.UserRequest) (*dto.UserResponse, error) {
return s.CreateUser(ctx, userInput(value))
}
func (s *UserService) UpdateUserRequest(ctx context.Context, value *dto.UserRequest) error {
return s.UpdateUser(ctx, userInput(value))
}
func (s *UserService) User(ctx context.Context, id uint) (map[string]any, error) {
func (s *UserService) User(ctx context.Context, id uint) (*dto.UserResponse, error) {
value, err := s.uc.User(ctx, id)
if err != nil {
return nil, err
@ -45,43 +45,39 @@ func (s *UserService) User(ctx context.Context, id uint) (map[string]any, error)
return convertUser(value), nil
}
func (s *UserService) Menus(ctx context.Context, authorityID uint) ([]map[string]any, error) {
func (s *UserService) Menus(ctx context.Context, authorityID uint) ([]*dto.MenuResponse, error) {
menus, err := s.uc.Menus(ctx, authorityID)
if err != nil {
return nil, err
}
result := make([]map[string]any, 0, len(menus))
for _, menu := range menus {
result = append(result, convertMenu(menu))
}
return result, nil
return menuResponses(menus), nil
}
func (s *UserService) ListUsers(ctx context.Context, page, pageSize int, filter *biz.UserListFilter) ([]map[string]any, int64, error) {
func (s *UserService) ListUsers(ctx context.Context, page, pageSize int, filter *biz.UserListFilter) ([]*dto.UserResponse, int64, error) {
users, total, err := s.uc.ListUsers(ctx, page, pageSize, filter)
if err != nil {
return nil, 0, err
}
result := make([]map[string]any, 0, len(users))
result := make([]*dto.UserResponse, 0, len(users))
for _, user := range users {
result = append(result, convertUser(user))
}
return result, total, nil
}
func (s *UserService) Authorities(ctx context.Context) ([]map[string]any, error) {
func (s *UserService) Authorities(ctx context.Context) ([]*dto.AuthorityResponse, error) {
values, err := s.uc.Authorities(ctx)
if err != nil {
return nil, err
}
result := make([]map[string]any, 0, len(values))
result := make([]*dto.AuthorityResponse, 0, len(values))
for _, value := range values {
result = append(result, convertAuthority(*value))
}
return result, nil
}
func (s *UserService) CreateUser(ctx context.Context, input UserInput) (map[string]any, error) {
func (s *UserService) CreateUser(ctx context.Context, input UserInput) (*dto.UserResponse, error) {
if err := s.settings.ValidatePassword(ctx, input.Password); err != nil {
return nil, err
}

View File

@ -1,56 +1,29 @@
package service
import "kra/internal/biz"
import (
"kra/internal/biz"
"kra/internal/service/dto"
)
func departmentDTO(v *biz.Department) map[string]any {
children := make([]map[string]any, 0, len(v.Children))
for _, c := range v.Children {
children = append(children, departmentDTO(c))
}
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "name": v.Name, "parentId": v.ParentID, "ancestors": v.Ancestors, "sort": v.Sort, "leaderId": v.LeaderID, "leader": nil, "status": v.Status, "children": children, "namePath": v.NamePath}
func convertAuthority(value biz.Authority) *dto.AuthorityResponse {
return &dto.AuthorityResponse{AuthorityID: value.AuthorityID, AuthorityName: value.AuthorityName, ParentID: value.ParentID, Children: []*dto.AuthorityResponse{}, Menus: []any{}, DataScope: value.DataScope, DefaultRouter: value.DefaultRouter}
}
func positionDTO(v *biz.Position) map[string]any {
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "name": v.Name, "code": v.Code, "sort": v.Sort, "status": v.Status, "remark": v.Remark}
}
func convertAuthority(value biz.Authority) map[string]any {
return map[string]any{"authorityId": value.AuthorityID, "authorityName": value.AuthorityName, "parentId": value.ParentID, "children": []map[string]any{}, "menus": []any{}, "dataScope": value.DataScope, "defaultRouter": value.DefaultRouter}
}
func convertUser(user *biz.User) map[string]any {
authorities := make([]map[string]any, 0, len(user.Authorities))
func convertUser(user *biz.User) *dto.UserResponse {
authorities := make([]*dto.AuthorityResponse, 0, len(user.Authorities))
for _, value := range user.Authorities {
authorities = append(authorities, convertAuthority(value))
}
departments := make([]map[string]any, 0, len(user.Departments))
departments := make([]*dto.DepartmentResponse, 0, len(user.Departments))
for i := range user.Departments {
departments = append(departments, departmentDTO(&user.Departments[i]))
departments = append(departments, departmentResponse(&user.Departments[i]))
}
positions := make([]map[string]any, 0, len(user.Positions))
positions := make([]*dto.PositionResponse, 0, len(user.Positions))
for i := range user.Positions {
positions = append(positions, positionDTO(&user.Positions[i]))
positions = append(positions, positionResponse(&user.Positions[i]))
}
department := map[string]any{}
var department any = map[string]any{}
if user.Department != nil {
department = departmentDTO(user.Department)
department = departmentResponse(user.Department)
}
return map[string]any{"ID": user.ID, "CreatedAt": user.CreatedAt, "UpdatedAt": user.UpdatedAt, "DeletedAt": nil, "uuid": user.UUID, "userName": user.Username, "nickName": user.NickName, "headerImg": user.HeaderImg, "authorityId": user.AuthorityID, "authority": convertAuthority(user.Authority), "authorities": authorities, "deptId": user.DeptID, "dept": department, "departments": departments, "positions": positions, "phone": user.Phone, "email": user.Email, "enable": user.Enable, "originSetting": user.OriginSetting}
}
func convertMenu(menu *biz.Menu) map[string]any {
children := make([]map[string]any, 0, len(menu.Children))
for _, child := range menu.Children {
children = append(children, convertMenu(child))
}
buttons := make([]map[string]any, 0, len(menu.Buttons))
buttonAuthorities := make(map[string]uint, len(menu.Buttons))
for _, button := range menu.Buttons {
buttons = append(buttons, map[string]any{"ID": button.ID, "name": button.Name, "desc": button.Description, "sysBaseMenuID": button.MenuID})
if button.AuthorityID != 0 {
buttonAuthorities[button.Name] = button.AuthorityID
}
}
parameters := make([]map[string]any, 0, len(menu.Parameters))
for _, parameter := range menu.Parameters {
parameters = append(parameters, map[string]any{"ID": parameter.ID, "SysBaseMenuID": parameter.MenuID, "type": parameter.Type, "key": parameter.Key, "value": parameter.Value})
}
return map[string]any{"ID": menu.ID, "parentId": menu.ParentID, "path": menu.Path, "name": menu.Name, "hidden": menu.Hidden, "component": menu.Component, "sort": menu.Sort, "meta": map[string]any{"activeName": menu.ActiveName, "keepAlive": menu.KeepAlive, "defaultMenu": menu.DefaultMenu, "title": menu.Title, "icon": menu.Icon, "closeTab": menu.CloseTab, "transitionType": menu.TransitionType}, "children": children, "parameters": parameters, "menuBtn": buttons, "btns": buttonAuthorities}
return &dto.UserResponse{ID: user.ID, CreatedAt: user.CreatedAt, UpdatedAt: user.UpdatedAt, DeletedAt: nil, UUID: user.UUID, Username: user.Username, NickName: user.NickName, HeaderImg: user.HeaderImg, AuthorityID: user.AuthorityID, Authority: convertAuthority(user.Authority), Authorities: authorities, DeptID: user.DeptID, Department: department, Departments: departments, Positions: positions, Phone: user.Phone, Email: user.Email, Enable: user.Enable, OriginSetting: user.OriginSetting}
}

View File

@ -12,21 +12,21 @@ import (
type VersionService struct{ uc *biz.VersionUsecase }
func NewVersionService(uc *biz.VersionUsecase) *VersionService { return &VersionService{uc: uc} }
func versionDTO(v *biz.Version) map[string]any {
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "versionName": v.Name, "versionCode": v.Code, "description": v.Description, "versionData": v.Data}
func versionDTO(v *biz.Version) *dto.VersionResponse {
return &dto.VersionResponse{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, DeletedAt: nil, VersionName: v.Name, VersionCode: v.Code, Description: v.Description, VersionData: v.Data}
}
func (s *VersionService) Versions(ctx context.Context, page, size int, name, code string, createdAtRange []*time.Time) ([]map[string]any, int64, error) {
func (s *VersionService) Versions(ctx context.Context, page, size int, name, code string, createdAtRange []*time.Time) ([]*dto.VersionResponse, int64, error) {
items, total, err := s.uc.ListVersions(ctx, page, size, name, code, createdAtRange)
if err != nil {
return nil, 0, err
}
out := make([]map[string]any, 0, len(items))
out := make([]*dto.VersionResponse, 0, len(items))
for _, v := range items {
out = append(out, versionDTO(v))
}
return out, total, nil
}
func (s *VersionService) Version(ctx context.Context, id uint) (map[string]any, error) {
func (s *VersionService) Version(ctx context.Context, id uint) (*dto.VersionResponse, error) {
v, err := s.uc.FindVersion(ctx, id)
if err != nil {
return nil, err
@ -53,15 +53,15 @@ func (s *VersionService) Export(ctx context.Context, name, code, description str
bundle.Code = code
bundle.Description = description
bundle.ExportTime = time.Now().Format("2006-01-02 15:04:05")
menus := make([]map[string]any, 0, len(bundle.Menus))
menus := make([]*dto.MenuResponse, 0, len(bundle.Menus))
for _, v := range bundle.Menus {
menus = append(menus, convertMenu(v))
menus = append(menus, menuResponse(v))
}
apis := make([]map[string]any, 0, len(bundle.APIs))
apis := make([]*dto.APIResponse, 0, len(bundle.APIs))
for _, v := range bundle.APIs {
apis = append(apis, apiDTO(v))
apis = append(apis, apiResponse(v))
}
dicts := make([]map[string]any, 0, len(bundle.Dictionaries))
dicts := make([]*dto.DictionaryResponse, 0, len(bundle.Dictionaries))
for _, v := range bundle.Dictionaries {
dicts = append(dicts, dictionaryDTO(v))
}

View File

@ -0,0 +1,6 @@
package worker
import "github.com/google/wire"
// ProviderSet contains background task runtime providers.
var ProviderSet = wire.NewSet(NewTaskExecutor, NewTaskScheduler, NewTaskRuntime)