fix: align admin behavior and Kratos integrations
This commit is contained in:
@@ -1 +1,3 @@
|
||||
# Service
|
||||
|
||||
本目录负责管理端 DTO 与领域对象之间的转换和输入校验,不直接访问数据库。固定 JSON 契约放在 `dto/`,只有用户设置、导出行等真正动态的数据使用 `map[string]any`。
|
||||
|
||||
+24
-96
@@ -2,7 +2,6 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/service/dto"
|
||||
@@ -15,54 +14,37 @@ func (s *AccessService) Authorize(ctx context.Context, aid uint, path, method st
|
||||
return s.uc.Authorize(ctx, aid, path, method)
|
||||
}
|
||||
|
||||
func authorityDTO(v *biz.Authority) map[string]any { return convertAuthority(*v) }
|
||||
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 departmentDTO(v *biz.Department) map[string]any {
|
||||
func authorityDTO(v *biz.Authority) map[string]any {
|
||||
out := convertAuthority(*v)
|
||||
children := make([]map[string]any, 0, len(v.Children))
|
||||
for _, c := range v.Children {
|
||||
children = append(children, departmentDTO(c))
|
||||
for _, child := range v.Children {
|
||||
children = append(children, authorityDTO(child))
|
||||
}
|
||||
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": ""}
|
||||
out["children"] = children
|
||||
return out
|
||||
}
|
||||
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 (s *AccessService) Authorities(ctx context.Context) ([]map[string]any, error) {
|
||||
items, err := s.uc.Repo().ListAuthorities(ctx)
|
||||
items, err := s.uc.AuthorityTree(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
byID := make(map[uint]map[string]any, len(items))
|
||||
roots := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
byID[v.AuthorityID] = authorityDTO(v)
|
||||
}
|
||||
roots := make([]map[string]any, 0)
|
||||
for _, v := range items {
|
||||
item := byID[v.AuthorityID]
|
||||
if v.ParentID != nil && *v.ParentID != 0 && byID[*v.ParentID] != nil {
|
||||
parent := byID[*v.ParentID]
|
||||
children, _ := parent["children"].([]map[string]any)
|
||||
parent["children"] = append(children, item)
|
||||
} else {
|
||||
roots = append(roots, item)
|
||||
}
|
||||
roots = append(roots, authorityDTO(v))
|
||||
}
|
||||
return roots, nil
|
||||
}
|
||||
func (s *AccessService) CreateAuthority(ctx context.Context, v *biz.Authority) error {
|
||||
return s.uc.Repo().CreateAuthority(ctx, v)
|
||||
return s.uc.CreateAuthority(ctx, v)
|
||||
}
|
||||
func (s *AccessService) CopyAuthority(ctx context.Context, sourceID uint, v *biz.Authority) error {
|
||||
return s.uc.Repo().CopyAuthority(ctx, sourceID, v)
|
||||
return s.uc.CopyAuthority(ctx, sourceID, v)
|
||||
}
|
||||
func (s *AccessService) UpdateAuthority(ctx context.Context, v *biz.Authority) error {
|
||||
return s.uc.Repo().UpdateAuthority(ctx, v)
|
||||
return s.uc.UpdateAuthority(ctx, v)
|
||||
}
|
||||
func (s *AccessService) DeleteAuthority(ctx context.Context, id uint) error {
|
||||
return s.uc.Repo().DeleteAuthority(ctx, id)
|
||||
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}
|
||||
@@ -92,19 +74,19 @@ func (s *AccessService) UpdateAuthorityRequest(ctx context.Context, req *dto.Aut
|
||||
return authorityResponse(value), nil
|
||||
}
|
||||
func (s *AccessService) SetAuthorityUsers(ctx context.Context, id uint, ids []uint) error {
|
||||
return s.uc.Repo().SetAuthorityUsers(ctx, id, ids)
|
||||
return s.uc.SetAuthorityUsers(ctx, id, ids)
|
||||
}
|
||||
func (s *AccessService) AuthorityUserIDs(ctx context.Context, id uint) ([]uint, error) {
|
||||
return s.uc.Repo().AuthorityUserIDs(ctx, id)
|
||||
return s.uc.AuthorityUserIDs(ctx, id)
|
||||
}
|
||||
func (s *AccessService) SetDataScope(ctx context.Context, id uint, scope int, ids []uint) error {
|
||||
return s.uc.Repo().SetDataScope(ctx, id, scope, ids)
|
||||
return s.uc.SetDataScope(ctx, id, scope, ids)
|
||||
}
|
||||
func (s *AccessService) DataScopeDepartmentIDs(ctx context.Context, id uint) ([]uint, error) {
|
||||
return s.uc.Repo().DataScopeDepartmentIDs(ctx, id)
|
||||
return s.uc.DataScopeDepartmentIDs(ctx, id)
|
||||
}
|
||||
func (s *AccessService) ResolveDataScope(ctx context.Context, authorityID, userID uint) (biz.DataScope, error) {
|
||||
return s.uc.Repo().ResolveDataScope(ctx, authorityID, userID)
|
||||
return s.uc.ResolveDataScope(ctx, authorityID, userID)
|
||||
}
|
||||
func (s *AccessService) ContextWithDataScope(ctx context.Context, authorityID, userID uint) (context.Context, error) {
|
||||
scope, err := s.ResolveDataScope(ctx, authorityID, userID)
|
||||
@@ -114,62 +96,8 @@ func (s *AccessService) ContextWithDataScope(ctx context.Context, authorityID, u
|
||||
return biz.NewDataScopeContext(ctx, scope), nil
|
||||
}
|
||||
|
||||
func (s *AccessService) DeleteAPIs(ctx context.Context, ids []uint) error {
|
||||
return s.uc.Repo().DeleteAPIs(ctx, ids)
|
||||
}
|
||||
func (s *AccessService) APIRoleIDs(ctx context.Context, id uint) ([]uint, error) {
|
||||
return s.uc.Repo().APIRoleIDs(ctx, id)
|
||||
}
|
||||
func (s *AccessService) SetAPIRoles(ctx context.Context, id uint, ids []uint) error {
|
||||
return s.uc.Repo().SetAPIRoles(ctx, id, ids)
|
||||
}
|
||||
func (s *AccessService) SyncAPIs(ctx context.Context, routes []*biz.API) (*dto.APISyncResponse, error) {
|
||||
stored, _, err := s.uc.Repo().ListAPIs(ctx, 1, 100000, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ignored, err := s.uc.Repo().IgnoredAPIs(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := func(v *biz.API) string { return strings.ToUpper(v.Method) + " " + v.Path }
|
||||
ignoreSet := make(map[string]bool, len(ignored))
|
||||
for _, item := range ignored {
|
||||
ignoreSet[key(item)] = true
|
||||
}
|
||||
routeSet := make(map[string]*biz.API, len(routes))
|
||||
for _, item := range routes {
|
||||
if !ignoreSet[key(item)] {
|
||||
routeSet[key(item)] = item
|
||||
}
|
||||
}
|
||||
storedSet := make(map[string]*biz.API, len(stored))
|
||||
for _, item := range stored {
|
||||
storedSet[key(item)] = item
|
||||
}
|
||||
added := make([]*dto.APIResponse, 0)
|
||||
deleted := make([]*dto.APIResponse, 0)
|
||||
ignoredDTO := make([]*dto.APIResponse, 0, len(ignored))
|
||||
for k, item := range routeSet {
|
||||
if storedSet[k] == nil {
|
||||
added = append(added, apiResponse(item))
|
||||
}
|
||||
}
|
||||
for k, item := range storedSet {
|
||||
if routeSet[k] == nil && !ignoreSet[k] {
|
||||
deleted = append(deleted, apiResponse(item))
|
||||
}
|
||||
}
|
||||
for _, item := range ignored {
|
||||
ignoredDTO = append(ignoredDTO, apiResponse(item))
|
||||
}
|
||||
return &dto.APISyncResponse{NewAPIs: added, DeleteAPIs: deleted, IgnoreAPIs: ignoredDTO}, nil
|
||||
}
|
||||
func (s *AccessService) SetAPIIgnored(ctx context.Context, path, method string, ignored bool) error {
|
||||
return s.uc.Repo().SetAPIIgnored(ctx, path, method, ignored)
|
||||
}
|
||||
func (s *AccessService) Buttons(ctx context.Context, id uint) ([]map[string]any, error) {
|
||||
items, err := s.uc.Repo().Buttons(ctx, id)
|
||||
items, err := s.uc.Buttons(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -180,17 +108,17 @@ func (s *AccessService) Buttons(ctx context.Context, id uint) ([]map[string]any,
|
||||
return out, nil
|
||||
}
|
||||
func (s *AccessService) SetAuthorityButtons(ctx context.Context, id uint, v map[uint][]uint) error {
|
||||
return s.uc.Repo().SetAuthorityButtons(ctx, id, v)
|
||||
return s.uc.SetAuthorityButtons(ctx, id, v)
|
||||
}
|
||||
func (s *AccessService) AuthorityButtonIDs(ctx context.Context, id uint) ([]uint, error) {
|
||||
return s.uc.Repo().AuthorityButtonIDs(ctx, id)
|
||||
return s.uc.AuthorityButtonIDs(ctx, id)
|
||||
}
|
||||
func (s *AccessService) SelectedButtons(ctx context.Context, aid, menuID uint) ([]uint, error) {
|
||||
return s.uc.Repo().SelectedButtons(ctx, aid, menuID)
|
||||
return s.uc.SelectedButtons(ctx, aid, menuID)
|
||||
}
|
||||
func (s *AccessService) SetSelectedButtons(ctx context.Context, aid, menuID uint, ids []uint) error {
|
||||
return s.uc.Repo().SetSelectedButtons(ctx, aid, menuID, ids)
|
||||
return s.uc.SetSelectedButtons(ctx, aid, menuID, ids)
|
||||
}
|
||||
func (s *AccessService) CanRemoveButton(ctx context.Context, id uint) (bool, error) {
|
||||
return s.uc.Repo().CanRemoveButton(ctx, id)
|
||||
return s.uc.CanRemoveButton(ctx, id)
|
||||
}
|
||||
|
||||
+37
-10
@@ -24,29 +24,33 @@ func apiResponses(items []*biz.API) []*dto.APIResponse {
|
||||
}
|
||||
|
||||
func (s *AccessService) ListAPI(ctx context.Context, req *dto.APIListRequest) ([]*dto.APIResponse, int64, error) {
|
||||
items, total, err := s.uc.Repo().ListAPIs(ctx, req.Page, req.PageSize, &biz.API{Path: req.Path, Description: req.Description, APIGroup: req.APIGroup, Method: req.Method})
|
||||
items, total, err := s.uc.ListAPIs(ctx, req.Page, req.PageSize, &biz.API{Path: req.Path, Description: req.Description, APIGroup: req.APIGroup, Method: req.Method, OrderKey: req.OrderKey, Desc: req.Desc})
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return apiResponses(items), total, nil
|
||||
}
|
||||
|
||||
func (s *AccessService) AllAPI(ctx context.Context) ([]*dto.APIResponse, error) {
|
||||
items, _, err := s.uc.Repo().ListAPIs(ctx, 1, 10000, nil)
|
||||
func (s *AccessService) AllAPI(ctx context.Context, strict bool) ([]*dto.APIResponse, error) {
|
||||
items, _, err := s.uc.ListAPIs(ctx, 0, 0, &biz.API{StrictAll: strict})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return apiResponses(items), nil
|
||||
}
|
||||
|
||||
func (s *AccessService) CreateAPIRequest(ctx context.Context, req *dto.APIRequest) error {
|
||||
return s.uc.Repo().CreateAPI(ctx, apiDomain(req))
|
||||
func (s *AccessService) CreateAPIRequest(ctx context.Context, req *dto.APIRequest) (*dto.APIResponse, error) {
|
||||
value := apiDomain(req)
|
||||
if err := s.uc.CreateAPI(ctx, value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return apiResponse(value), nil
|
||||
}
|
||||
func (s *AccessService) UpdateAPIRequest(ctx context.Context, req *dto.APIRequest) error {
|
||||
return s.uc.Repo().UpdateAPI(ctx, apiDomain(req))
|
||||
return s.uc.UpdateAPI(ctx, apiDomain(req))
|
||||
}
|
||||
func (s *AccessService) FindAPIResponse(ctx context.Context, id uint) (*dto.APIResponse, error) {
|
||||
value, err := s.uc.Repo().FindAPI(ctx, id)
|
||||
value, err := s.uc.FindAPI(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -61,17 +65,17 @@ func (s *AccessService) ApplyAPISyncRequest(ctx context.Context, req *dto.ApplyA
|
||||
for i := range req.DeleteAPIs {
|
||||
deleted = append(deleted, apiDomain(&req.DeleteAPIs[i]))
|
||||
}
|
||||
return s.uc.Repo().ApplyAPISync(ctx, added, deleted)
|
||||
return s.uc.ApplyAPISync(ctx, added, deleted)
|
||||
}
|
||||
func (s *AccessService) SetPolicyPathsRequest(ctx context.Context, req *dto.SetPolicyPathsRequest) error {
|
||||
paths := make([]*biz.API, 0, len(req.Infos))
|
||||
for _, value := range req.Infos {
|
||||
paths = append(paths, &biz.API{Path: value.Path, Method: value.Method})
|
||||
}
|
||||
return s.uc.Repo().SetPolicyPaths(ctx, req.AuthorityID, paths)
|
||||
return s.uc.SetPolicyPaths(ctx, req.AuthorityID, paths)
|
||||
}
|
||||
func (s *AccessService) PolicyPathResponses(ctx context.Context, authorityID uint) ([]dto.PolicyPathRequest, error) {
|
||||
items, err := s.uc.Repo().PolicyPaths(ctx, authorityID)
|
||||
items, err := s.uc.PolicyPaths(ctx, authorityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -88,3 +92,26 @@ func (s *AccessService) SyncAPIResponses(ctx context.Context, routes []dto.APIRe
|
||||
}
|
||||
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 *AccessService) DeleteAPIs(ctx context.Context, ids []uint) error {
|
||||
return s.uc.DeleteAPIs(ctx, ids)
|
||||
}
|
||||
func (s *AccessService) APIRoleIDs(ctx context.Context, path, method string) ([]uint, error) {
|
||||
return s.uc.APIRoleIDs(ctx, path, method)
|
||||
}
|
||||
func (s *AccessService) SetAPIRoles(ctx context.Context, path, method string, ids []uint) error {
|
||||
return s.uc.SetAPIRoles(ctx, path, method, ids)
|
||||
}
|
||||
func (s *AccessService) SyncAPIs(ctx context.Context, routes []*biz.API) (*dto.APISyncResponse, error) {
|
||||
diff, err := s.uc.SyncAPIs(ctx, routes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &dto.APISyncResponse{NewAPIs: apiResponses(diff.Added), DeleteAPIs: apiResponses(diff.Deleted), IgnoreAPIs: apiResponses(diff.Ignored)}, nil
|
||||
}
|
||||
func (s *AccessService) SetAPIIgnored(ctx context.Context, path, method string, ignored bool) error {
|
||||
return s.uc.SetAPIIgnored(ctx, path, method, ignored)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
@@ -18,29 +17,20 @@ func tokenDTO(v *biz.APIToken) map[string]any {
|
||||
}
|
||||
|
||||
func (s *SettingsService) CreateAPIToken(ctx context.Context, userID, authorityID uint, days int, remark string) (string, error) {
|
||||
user, allowed, err := s.Repo().UserHasAuthority(ctx, userID, authorityID)
|
||||
user, duration, err := s.uc.PrepareAPIToken(ctx, userID, authorityID, days)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !allowed {
|
||||
return "", errors.New("用户不具备该角色权限")
|
||||
}
|
||||
duration := time.Duration(days) * 24 * time.Hour
|
||||
if days == -1 {
|
||||
duration = 100 * 365 * 24 * time.Hour
|
||||
}
|
||||
if duration <= 0 {
|
||||
return "", errors.New("有效天数必须大于0或为-1")
|
||||
}
|
||||
secret, issuer := "", "kra"
|
||||
buffer := 24 * time.Hour
|
||||
if s.config != nil && s.config.Jwt != nil {
|
||||
secret = s.config.Jwt.SigningKey
|
||||
if s.config.Jwt.Issuer != "" {
|
||||
issuer = s.config.Jwt.Issuer
|
||||
config := s.runtime.Admin()
|
||||
if config != nil && config.Jwt != nil {
|
||||
secret = config.Jwt.SigningKey
|
||||
if config.Jwt.Issuer != "" {
|
||||
issuer = config.Jwt.Issuer
|
||||
}
|
||||
if s.config.Jwt.BufferTime != nil {
|
||||
buffer = s.config.Jwt.BufferTime.AsDuration()
|
||||
if config.Jwt.BufferTime != nil {
|
||||
buffer = config.Jwt.BufferTime.AsDuration()
|
||||
}
|
||||
}
|
||||
token, _, err := adminauth.Generate(secret, issuer, duration, buffer, user.ID, authorityID, user.UUID, user.Username, user.NickName, false)
|
||||
@@ -48,13 +38,13 @@ func (s *SettingsService) CreateAPIToken(ctx context.Context, userID, authorityI
|
||||
return "", err
|
||||
}
|
||||
record := &biz.APIToken{UserID: userID, AuthorityID: authorityID, Token: token, Status: true, ExpiresAt: time.Now().Add(duration), Remark: remark}
|
||||
if err = s.Repo().CreateAPIToken(ctx, record); err != nil {
|
||||
if err = s.uc.CreateAPIToken(ctx, record); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
func (s *SettingsService) APITokens(ctx context.Context, page, size int, userID uint, status *bool) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.Repo().ListAPITokens(ctx, page, size, userID, status)
|
||||
items, total, err := s.uc.ListAPITokens(ctx, page, size, userID, status)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -65,15 +55,11 @@ func (s *SettingsService) APITokens(ctx context.Context, page, size int, userID
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *SettingsService) DisableAPIToken(ctx context.Context, id uint) error {
|
||||
token, err := s.Repo().DisableAPIToken(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.Repo().BlacklistToken(ctx, token)
|
||||
return s.uc.DisableToken(ctx, id)
|
||||
}
|
||||
func (s *SettingsService) BlacklistToken(ctx context.Context, token string) error {
|
||||
return s.Repo().BlacklistToken(ctx, token)
|
||||
return s.uc.BlacklistToken(ctx, token)
|
||||
}
|
||||
func (s *SettingsService) IsTokenDisabled(ctx context.Context, token string) (bool, error) {
|
||||
return s.Repo().IsTokenDisabled(ctx, token)
|
||||
return s.uc.IsTokenDisabled(ctx, token)
|
||||
}
|
||||
|
||||
+48
-107
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/service/dto"
|
||||
)
|
||||
@@ -9,44 +10,25 @@ import (
|
||||
type AuditService struct{ uc *biz.AuditUsecase }
|
||||
|
||||
func NewAuditService(uc *biz.AuditUsecase) *AuditService { return &AuditService{uc: uc} }
|
||||
|
||||
func (s *AuditService) RecordOperation(ctx context.Context, v *biz.OperationRecord) error {
|
||||
return s.uc.Repo().RecordOperation(ctx, v)
|
||||
return s.uc.RecordOperation(ctx, v)
|
||||
}
|
||||
func (s *AuditService) 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, ip string, userID uint) ([]map[string]any, int64, error) {
|
||||
return s.Operations(ctx, page, size, &biz.OperationRecord{Path: path, Method: method, Status: status, IP: ip, UserID: userID})
|
||||
}
|
||||
func (s *AuditService) RecordLogin(ctx context.Context, v *biz.LoginLog) error {
|
||||
return s.uc.Repo().RecordLogin(ctx, v)
|
||||
}
|
||||
func (s *AuditService) 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, ip string) ([]map[string]any, int64, error) {
|
||||
return s.Logins(ctx, page, size, &biz.LoginLog{Username: username, IP: ip})
|
||||
}
|
||||
func (s *AuditService) RecordDataAccess(ctx context.Context, v *biz.DataAccessLog) error {
|
||||
return s.uc.Repo().RecordDataAccess(ctx, v)
|
||||
}
|
||||
func (s *AuditService) 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) {
|
||||
return s.DataAccess(ctx, req.Page, req.PageSize, &biz.DataAccessLog{EventType: req.EventType, TargetTable: req.TargetTable, UserID: req.UserID})
|
||||
func (s *AuditService) OperationsFilter(ctx context.Context, page, size int, path, method string, status int) ([]map[string]any, int64, error) {
|
||||
return s.Operations(ctx, page, size, &biz.OperationRecord{Path: path, Method: method, Status: status})
|
||||
}
|
||||
func opDTO(v *biz.OperationRecord) map[string]any {
|
||||
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": nil}
|
||||
}
|
||||
func loginDTO(v *biz.LoginLog) map[string]any {
|
||||
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": nil}
|
||||
}
|
||||
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}
|
||||
user := 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}
|
||||
}
|
||||
func (s *AuditService) Operations(ctx context.Context, page, size int, q *biz.OperationRecord) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.Repo().ListOperations(ctx, page, size, q)
|
||||
items, total, err := s.uc.ListOperations(ctx, page, size, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -57,17 +39,34 @@ func (s *AuditService) Operations(ctx context.Context, page, size int, q *biz.Op
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *AuditService) Operation(ctx context.Context, id uint) (map[string]any, error) {
|
||||
v, err := s.uc.Repo().FindOperation(ctx, id)
|
||||
v, err := s.uc.FindOperation(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return opDTO(v), nil
|
||||
}
|
||||
func (s *AuditService) DeleteOperations(ctx context.Context, ids []uint) error {
|
||||
return s.uc.Repo().DeleteOperations(ctx, ids)
|
||||
return s.uc.DeleteOperations(ctx, ids)
|
||||
}
|
||||
|
||||
func (s *AuditService) RecordLogin(ctx context.Context, v *biz.LoginLog) error {
|
||||
return s.uc.RecordLogin(ctx, v)
|
||||
}
|
||||
func (s *AuditService) 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) {
|
||||
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{}
|
||||
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}
|
||||
}
|
||||
func (s *AuditService) Logins(ctx context.Context, page, size int, q *biz.LoginLog) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.Repo().ListLogins(ctx, page, size, q)
|
||||
items, total, err := s.uc.ListLogins(ctx, page, size, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -78,17 +77,30 @@ func (s *AuditService) Logins(ctx context.Context, page, size int, q *biz.LoginL
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *AuditService) Login(ctx context.Context, id uint) (map[string]any, error) {
|
||||
v, err := s.uc.Repo().FindLogin(ctx, id)
|
||||
v, err := s.uc.FindLogin(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return loginDTO(v), nil
|
||||
}
|
||||
func (s *AuditService) DeleteLogins(ctx context.Context, ids []uint) error {
|
||||
return s.uc.Repo().DeleteLogins(ctx, ids)
|
||||
return s.uc.DeleteLogins(ctx, ids)
|
||||
}
|
||||
|
||||
func (s *AuditService) RecordDataAccess(ctx context.Context, v *biz.DataAccessLog) error {
|
||||
return s.uc.RecordDataAccess(ctx, v)
|
||||
}
|
||||
func (s *AuditService) 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) {
|
||||
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 (s *AuditService) DataAccess(ctx context.Context, page, size int, q *biz.DataAccessLog) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.Repo().ListDataAccess(ctx, page, size, q)
|
||||
items, total, err := s.uc.ListDataAccess(ctx, page, size, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -99,76 +111,5 @@ func (s *AuditService) DataAccess(ctx context.Context, page, size int, q *biz.Da
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *AuditService) DeleteDataAccess(ctx context.Context, ids []uint) error {
|
||||
return s.uc.Repo().DeleteDataAccess(ctx, ids)
|
||||
}
|
||||
func (s *AuditService) LogDates(ctx context.Context, month string) (map[string]any, error) {
|
||||
items, err := s.uc.Repo().LogDates(ctx, month)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, map[string]any{"date": v.Date, "fileCount": v.FileCount})
|
||||
}
|
||||
return map[string]any{"month": month, "dates": out}, nil
|
||||
}
|
||||
func (s *AuditService) LogFiles(ctx context.Context, date string) (map[string]any, error) {
|
||||
items, err := s.uc.Repo().LogFiles(ctx, date)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]map[string]any, 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})
|
||||
}
|
||||
return map[string]any{"date": date, "files": out}, nil
|
||||
}
|
||||
func (s *AuditService) LogContent(ctx context.Context, date, path string, cursor *int64) (map[string]any, error) {
|
||||
v, err := s.uc.Repo().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
|
||||
}
|
||||
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 (s *AuditService) CreateError(ctx context.Context, v *biz.ErrorRecord) error {
|
||||
return s.uc.Repo().CreateError(ctx, v)
|
||||
}
|
||||
func errorDomain(value *dto.ErrorRecordRequest) *biz.ErrorRecord {
|
||||
return &biz.ErrorRecord{ID: value.ID, Form: value.Form, Info: value.Info, Level: value.Level, RequestID: value.RequestID, TraceID: value.TraceID, Solution: value.Solution, Status: value.Status}
|
||||
}
|
||||
func (s *AuditService) CreateErrorRequest(ctx context.Context, req *dto.ErrorRecordRequest) error {
|
||||
return s.CreateError(ctx, errorDomain(req))
|
||||
}
|
||||
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, level, status string) ([]map[string]any, int64, error) {
|
||||
return s.Errors(ctx, page, size, &biz.ErrorRecord{Form: form, Level: level, Status: status})
|
||||
}
|
||||
func (s *AuditService) UpdateError(ctx context.Context, v *biz.ErrorRecord) error {
|
||||
return s.uc.Repo().UpdateError(ctx, v)
|
||||
}
|
||||
func (s *AuditService) DeleteErrors(ctx context.Context, ids []uint) error {
|
||||
return s.uc.Repo().DeleteErrors(ctx, ids)
|
||||
}
|
||||
func (s *AuditService) Error(ctx context.Context, id uint) (map[string]any, error) {
|
||||
v, err := s.uc.Repo().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) {
|
||||
items, total, err := s.uc.Repo().ListErrors(ctx, page, size, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, errorDTO(v))
|
||||
}
|
||||
return out, total, nil
|
||||
return s.uc.DeleteDataAccess(ctx, ids)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"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 (s *AuditService) CreateError(ctx context.Context, v *biz.ErrorRecord) error {
|
||||
return s.uc.CreateError(ctx, v)
|
||||
}
|
||||
func errorDomain(value *dto.ErrorRecordRequest) *biz.ErrorRecord {
|
||||
return &biz.ErrorRecord{ID: value.ID, Form: value.Form, Info: value.Info, Level: value.Level, RequestID: value.RequestID, TraceID: value.TraceID, Solution: value.Solution, Status: value.Status}
|
||||
}
|
||||
func (s *AuditService) CreateErrorRequest(ctx context.Context, req *dto.ErrorRecordRequest) error {
|
||||
return s.CreateError(ctx, errorDomain(req))
|
||||
}
|
||||
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) {
|
||||
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 {
|
||||
return s.uc.UpdateError(ctx, v)
|
||||
}
|
||||
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) {
|
||||
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) {
|
||||
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))
|
||||
for _, v := range items {
|
||||
out = append(out, errorDTO(v))
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
func (s *AuditService) LogDates(ctx context.Context, month string) (map[string]any, error) {
|
||||
items, err := s.uc.LogDates(ctx, month)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, map[string]any{"date": v.Date, "fileCount": v.FileCount})
|
||||
}
|
||||
return map[string]any{"month": month, "dates": out}, nil
|
||||
}
|
||||
func (s *AuditService) LogFiles(ctx context.Context, date string) (map[string]any, error) {
|
||||
items, err := s.uc.LogFiles(ctx, date)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]map[string]any, 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})
|
||||
}
|
||||
return map[string]any{"date": date, "files": out}, nil
|
||||
}
|
||||
func (s *AuditService) LogContent(ctx context.Context, date, path string, cursor *int64) (map[string]any, 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
|
||||
}
|
||||
@@ -34,29 +34,39 @@ func (s *SystemService) Login(ctx context.Context, username, password string) (*
|
||||
return nil, err
|
||||
}
|
||||
if u.Enable != 1 {
|
||||
return nil, biz.ErrInvalidCredentials
|
||||
return nil, biz.ErrUserDisabled
|
||||
}
|
||||
if security, securityErr := s.settings.CurrentSecurity(ctx); securityErr == nil && security.PwdExpireEnable && u.PasswordUpdatedAt != nil && time.Since(*u.PasswordUpdatedAt) > time.Duration(security.PwdExpireDays)*24*time.Hour {
|
||||
u.MustChangePassword = true
|
||||
}
|
||||
return s.issueLogin(u, u.AuthorityID)
|
||||
return s.issueLogin(ctx, u, u.AuthorityID)
|
||||
}
|
||||
|
||||
func (s *SystemService) issueLogin(user *biz.User, authorityID uint) (*LoginResult, error) {
|
||||
func (s *SystemService) issueLogin(ctx context.Context, user *biz.User, authorityID uint) (*LoginResult, error) {
|
||||
expires, buffer := 7*24*time.Hour, 24*time.Hour
|
||||
secret, issuer := "", "kra"
|
||||
if s.config != nil && s.config.Jwt != nil {
|
||||
secret, issuer = s.config.Jwt.SigningKey, s.config.Jwt.Issuer
|
||||
if s.config.Jwt.ExpiresTime != nil {
|
||||
expires = s.config.Jwt.ExpiresTime.AsDuration()
|
||||
config := s.runtime.Admin()
|
||||
if config != nil && config.Jwt != nil {
|
||||
secret, issuer = config.Jwt.SigningKey, config.Jwt.Issuer
|
||||
if config.Jwt.ExpiresTime != nil {
|
||||
expires = config.Jwt.ExpiresTime.AsDuration()
|
||||
}
|
||||
if s.config.Jwt.BufferTime != nil {
|
||||
buffer = s.config.Jwt.BufferTime.AsDuration()
|
||||
if config.Jwt.BufferTime != nil {
|
||||
buffer = config.Jwt.BufferTime.AsDuration()
|
||||
}
|
||||
}
|
||||
token, claims, err := adminauth.Generate(secret, issuer, expires, buffer, user.ID, authorityID, user.UUID, user.Username, user.NickName, user.MustChangePassword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s.settings.UseMultipoint() {
|
||||
oldToken, _, cacheErr := s.settings.cache.Get(ctx, activeTokenKey(user.Username))
|
||||
if cacheErr != nil {
|
||||
return nil, cacheErr
|
||||
}
|
||||
if cacheErr = s.settings.RotateActiveToken(ctx, user.Username, oldToken, token, expires); cacheErr != nil {
|
||||
return nil, cacheErr
|
||||
}
|
||||
}
|
||||
return &LoginResult{User: convertUser(user), Token: token, ExpiresAt: claims.ExpiresAt.UnixMilli(), NeedChangePassword: user.MustChangePassword}, nil
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/service/dto"
|
||||
@@ -16,25 +14,12 @@ 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 detailDomain(value *dto.DictionaryDetailRequest) *biz.DictionaryDetail {
|
||||
status := true
|
||||
if value.Status != nil {
|
||||
status = *value.Status
|
||||
}
|
||||
return &biz.DictionaryDetail{ID: value.ID, Label: value.Label, Value: value.Value, Extend: value.Extend, Status: status, Sort: value.Sort, DictionaryID: value.DictionaryID, ParentID: value.ParentID, Level: value.Level, Path: value.Path}
|
||||
}
|
||||
func (s *SettingsService) CreateDictionaryRequest(ctx context.Context, req *dto.DictionaryRequest) error {
|
||||
return s.CreateDictionary(ctx, dictionaryDomain(req))
|
||||
}
|
||||
func (s *SettingsService) UpdateDictionaryRequest(ctx context.Context, req *dto.DictionaryRequest) error {
|
||||
return s.UpdateDictionary(ctx, dictionaryDomain(req))
|
||||
}
|
||||
func (s *SettingsService) CreateDictionaryDetailRequest(ctx context.Context, req *dto.DictionaryDetailRequest) error {
|
||||
return s.CreateDictionaryDetail(ctx, detailDomain(req))
|
||||
}
|
||||
func (s *SettingsService) UpdateDictionaryDetailRequest(ctx context.Context, req *dto.DictionaryDetailRequest) error {
|
||||
return s.UpdateDictionaryDetail(ctx, detailDomain(req))
|
||||
}
|
||||
func dictionaryDTO(v *biz.Dictionary) map[string]any {
|
||||
children := make([]map[string]any, 0, len(v.Children))
|
||||
for _, x := range v.Children {
|
||||
@@ -46,15 +31,8 @@ func dictionaryDTO(v *biz.Dictionary) map[string]any {
|
||||
}
|
||||
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}
|
||||
}
|
||||
func detailDTO(v *biz.DictionaryDetail) map[string]any {
|
||||
children := make([]map[string]any, 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}
|
||||
}
|
||||
func (s *SettingsService) Dictionaries(ctx context.Context, page, size int, name, typ string, details bool) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.Repo().ListDictionaries(ctx, page, size, name, typ, details)
|
||||
items, total, err := s.uc.ListDictionaries(ctx, page, size, name, typ, details)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -64,24 +42,52 @@ func (s *SettingsService) Dictionaries(ctx context.Context, page, size int, name
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *SettingsService) Dictionary(ctx context.Context, id uint, typ string, details bool) (map[string]any, error) {
|
||||
v, err := s.Repo().FindDictionary(ctx, id, typ, details)
|
||||
func (s *SettingsService) Dictionary(ctx context.Context, id uint, typ string, status *bool, details bool) (map[string]any, error) {
|
||||
v, err := s.uc.FindDictionary(ctx, id, typ, status, details)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dictionaryDTO(v), nil
|
||||
}
|
||||
func (s *SettingsService) ExportDictionary(ctx context.Context, id uint) (map[string]any, error) {
|
||||
v, err := s.uc.ExportDictionary(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dictionaryDTO(v), nil
|
||||
}
|
||||
func (s *SettingsService) CreateDictionary(ctx context.Context, v *biz.Dictionary) error {
|
||||
return s.Repo().CreateDictionary(ctx, v)
|
||||
return s.uc.CreateDictionary(ctx, v)
|
||||
}
|
||||
func (s *SettingsService) UpdateDictionary(ctx context.Context, v *biz.Dictionary) error {
|
||||
return s.Repo().UpdateDictionary(ctx, v)
|
||||
return s.uc.UpdateDictionary(ctx, v)
|
||||
}
|
||||
func (s *SettingsService) DeleteDictionary(ctx context.Context, id uint) error {
|
||||
return s.Repo().DeleteDictionary(ctx, id)
|
||||
return s.uc.DeleteDictionary(ctx, id)
|
||||
}
|
||||
func (s *SettingsService) DictionaryDetails(ctx context.Context, page, size int, dictionaryID uint, label, value string) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.Repo().ListDictionaryDetails(ctx, page, size, dictionaryID, label, value)
|
||||
|
||||
func detailDomain(value *dto.DictionaryDetailRequest) *biz.DictionaryDetail {
|
||||
status := true
|
||||
if value.Status != nil {
|
||||
status = *value.Status
|
||||
}
|
||||
return &biz.DictionaryDetail{ID: value.ID, Label: value.Label, Value: value.Value, Extend: value.Extend, Status: status, Sort: value.Sort, DictionaryID: value.DictionaryID, ParentID: value.ParentID, Level: value.Level, Path: value.Path}
|
||||
}
|
||||
func (s *SettingsService) CreateDictionaryDetailRequest(ctx context.Context, req *dto.DictionaryDetailRequest) error {
|
||||
return s.CreateDictionaryDetail(ctx, detailDomain(req))
|
||||
}
|
||||
func (s *SettingsService) 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))
|
||||
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}
|
||||
}
|
||||
func (s *SettingsService) DictionaryDetails(ctx context.Context, page, size int, filter biz.DictionaryDetailFilter) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.ListDictionaryDetails(ctx, page, size, filter)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -92,14 +98,14 @@ func (s *SettingsService) DictionaryDetails(ctx context.Context, page, size int,
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *SettingsService) DictionaryDetail(ctx context.Context, id uint) (map[string]any, error) {
|
||||
v, err := s.Repo().FindDictionaryDetail(ctx, id)
|
||||
v, err := s.uc.FindDictionaryDetail(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return detailDTO(v), nil
|
||||
}
|
||||
func (s *SettingsService) DictionaryTree(ctx context.Context, id uint, typ string) ([]map[string]any, error) {
|
||||
items, err := s.Repo().DictionaryDetailTree(ctx, id, typ)
|
||||
items, err := s.uc.DictionaryDetailTree(ctx, id, typ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -110,73 +116,11 @@ func (s *SettingsService) DictionaryTree(ctx context.Context, id uint, typ strin
|
||||
return out, nil
|
||||
}
|
||||
func (s *SettingsService) CreateDictionaryDetail(ctx context.Context, v *biz.DictionaryDetail) error {
|
||||
return s.Repo().CreateDictionaryDetail(ctx, v)
|
||||
return s.uc.CreateDictionaryDetail(ctx, v)
|
||||
}
|
||||
func (s *SettingsService) UpdateDictionaryDetail(ctx context.Context, v *biz.DictionaryDetail) error {
|
||||
return s.Repo().UpdateDictionaryDetail(ctx, v)
|
||||
return s.uc.UpdateDictionaryDetail(ctx, v)
|
||||
}
|
||||
func (s *SettingsService) DeleteDictionaryDetail(ctx context.Context, id uint) error {
|
||||
return s.Repo().DeleteDictionaryDetail(ctx, id)
|
||||
}
|
||||
func (s *SettingsService) ImportDictionaryJSON(ctx context.Context, raw string) error {
|
||||
var payload struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Status *bool `json:"status"`
|
||||
Description string `json:"desc"`
|
||||
Details []dto.DictionaryDetailRequest `json:"sysDictionaryDetails"`
|
||||
}
|
||||
if json.Unmarshal([]byte(raw), &payload) != nil || payload.Name == "" || payload.Type == "" {
|
||||
return errors.New("JSON 格式错误")
|
||||
}
|
||||
dictionary := dictionaryDomain(&dto.DictionaryRequest{Name: payload.Name, Type: payload.Type, Status: payload.Status, Description: payload.Description})
|
||||
if err := s.CreateDictionary(ctx, dictionary); err != nil {
|
||||
return err
|
||||
}
|
||||
mapping := map[uint]uint{}
|
||||
pending := append([]dto.DictionaryDetailRequest(nil), payload.Details...)
|
||||
for len(pending) > 0 {
|
||||
progress := false
|
||||
next := pending[:0]
|
||||
for _, item := range pending {
|
||||
oldID := item.ID
|
||||
if item.ParentID != nil && *item.ParentID != 0 {
|
||||
parent, exists := mapping[*item.ParentID]
|
||||
if !exists {
|
||||
next = append(next, item)
|
||||
continue
|
||||
}
|
||||
item.ParentID = &parent
|
||||
}
|
||||
value := detailDomain(&item)
|
||||
value.ID = 0
|
||||
value.DictionaryID = dictionary.ID
|
||||
if err := s.CreateDictionaryDetail(ctx, value); err != nil {
|
||||
return errors.New("导入详情失败")
|
||||
}
|
||||
if oldID != 0 {
|
||||
mapping[oldID] = value.ID
|
||||
}
|
||||
progress = true
|
||||
}
|
||||
if !progress {
|
||||
return errors.New("字典层级关系无效")
|
||||
}
|
||||
pending = next
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (s *SettingsService) DictionaryDetailsByParent(ctx context.Context, dictionaryID, parentID uint) ([]map[string]any, error) {
|
||||
items, _, err := s.DictionaryDetails(ctx, 1, 10000, dictionaryID, "", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := []map[string]any{}
|
||||
for _, item := range items {
|
||||
parent, _ := item["parentID"].(*uint)
|
||||
if (parentID == 0 && parent == nil) || (parent != nil && *parent == parentID) {
|
||||
out = append(out, item)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
return s.uc.DeleteDictionaryDetail(ctx, id)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/service/dto"
|
||||
)
|
||||
|
||||
func (s *SettingsService) ImportDictionaryJSON(ctx context.Context, raw string) error {
|
||||
var payload struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Status *bool `json:"status"`
|
||||
Description string `json:"desc"`
|
||||
Details []dto.DictionaryDetailRequest `json:"sysDictionaryDetails"`
|
||||
}
|
||||
if json.Unmarshal([]byte(raw), &payload) != nil || payload.Name == "" || payload.Type == "" {
|
||||
return errors.New("JSON 格式错误")
|
||||
}
|
||||
dictionary := dictionaryDomain(&dto.DictionaryRequest{Name: payload.Name, Type: payload.Type, Status: payload.Status, Description: payload.Description})
|
||||
details := make([]*biz.DictionaryDetail, 0, len(payload.Details))
|
||||
for i := range payload.Details {
|
||||
details = append(details, detailDomain(&payload.Details[i]))
|
||||
}
|
||||
return s.uc.ImportDictionary(ctx, dictionary, details)
|
||||
}
|
||||
func (s *SettingsService) DictionaryDetailsByParent(ctx context.Context, dictionaryID, parentID uint) ([]map[string]any, error) {
|
||||
items, err := s.uc.DictionaryDetailsByParent(ctx, dictionaryID, parentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, item := range items {
|
||||
out = append(out, detailDTO(item))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
@@ -17,6 +17,8 @@ type APIListRequest struct {
|
||||
Description string `json:"description"`
|
||||
APIGroup string `json:"apiGroup"`
|
||||
Method string `json:"method"`
|
||||
OrderKey string `json:"orderKey"`
|
||||
Desc bool `json:"desc"`
|
||||
}
|
||||
|
||||
type APIResponse struct {
|
||||
@@ -40,7 +42,8 @@ type GetAPIRequest struct {
|
||||
ID uint `json:"id"`
|
||||
}
|
||||
type SetAPIRolesRequest struct {
|
||||
APIID uint `json:"apiId"`
|
||||
Path string `json:"path"`
|
||||
Method string `json:"method"`
|
||||
AuthorityIDs []uint `json:"authorityIds"`
|
||||
}
|
||||
type IgnoreAPIRequest struct {
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
package dto
|
||||
|
||||
import "time"
|
||||
|
||||
type MediaListRequest struct {
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
Keyword string `json:"keyword"`
|
||||
ClassID int `json:"classId"`
|
||||
Tag string `json:"tag"`
|
||||
UserID uint `json:"userId"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
Keyword string `json:"keyword"`
|
||||
ClassID int `json:"classId"`
|
||||
Tag string `json:"tag"`
|
||||
UserID uint `json:"userId"`
|
||||
StartCreatedAt time.Time `json:"startCreatedAt"`
|
||||
EndCreatedAt time.Time `json:"endCreatedAt"`
|
||||
OrderKey string `json:"orderKey"`
|
||||
Desc bool `json:"desc"`
|
||||
}
|
||||
type IDRequest struct {
|
||||
ID uint `json:"ID" form:"id"`
|
||||
@@ -27,6 +33,7 @@ type ImportMediaRequest struct {
|
||||
Size int64 `json:"size"`
|
||||
Mime string `json:"mime"`
|
||||
MD5 string `json:"md5"`
|
||||
UserID uint `json:"userId"`
|
||||
}
|
||||
type StorageListRequest struct {
|
||||
Prefix string `json:"prefix"`
|
||||
|
||||
@@ -57,6 +57,7 @@ type PositionListRequest struct {
|
||||
PageSize int `json:"pageSize"`
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
Status *bool `json:"status"`
|
||||
}
|
||||
type DeletePositionRequest struct {
|
||||
ID uint `json:"ID"`
|
||||
|
||||
@@ -41,6 +41,7 @@ type SelfUserRequest struct {
|
||||
HeaderImg string `json:"headerImg"`
|
||||
Phone string `json:"phone"`
|
||||
Email string `json:"email"`
|
||||
Enable int `json:"enable"`
|
||||
}
|
||||
|
||||
type ResetPasswordRequest struct {
|
||||
|
||||
@@ -27,12 +27,45 @@ 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"`
|
||||
} `json:"database"`
|
||||
Redis *struct {
|
||||
Network string `json:"network"`
|
||||
Addr string `json:"addr"`
|
||||
ReadTimeout string `json:"read_timeout"`
|
||||
WriteTimeout string `json:"write_timeout"`
|
||||
} `json:"redis"`
|
||||
} `json:"data"`
|
||||
Admin struct {
|
||||
RouterPrefix string `json:"routerPrefix"`
|
||||
JWT struct {
|
||||
SigningKey string `json:"signingKey"`
|
||||
Issuer string `json:"issuer"`
|
||||
System struct {
|
||||
UseRedis bool `json:"useRedis"`
|
||||
UseMultipoint bool `json:"useMultipoint"`
|
||||
UseStrictAuth bool `json:"useStrictAuth"`
|
||||
DisableAutoMigrate bool `json:"disableAutoMigrate"`
|
||||
} `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"`
|
||||
|
||||
@@ -13,15 +13,21 @@ type VersionMenuButtonRequest struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"desc"`
|
||||
}
|
||||
type VersionMenuParameterRequest struct {
|
||||
Type string `json:"type"`
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
type VersionMenuRequest struct {
|
||||
Path string `json:"path"`
|
||||
Name string `json:"name"`
|
||||
Hidden bool `json:"hidden"`
|
||||
Component string `json:"component"`
|
||||
Sort int `json:"sort"`
|
||||
Meta VersionMenuMetaRequest `json:"meta"`
|
||||
MenuButtons []VersionMenuButtonRequest `json:"menuBtn"`
|
||||
Children []VersionMenuRequest `json:"children"`
|
||||
Path string `json:"path"`
|
||||
Name string `json:"name"`
|
||||
Hidden bool `json:"hidden"`
|
||||
Component string `json:"component"`
|
||||
Sort int `json:"sort"`
|
||||
Meta VersionMenuMetaRequest `json:"meta"`
|
||||
MenuButtons []VersionMenuButtonRequest `json:"menuBtn"`
|
||||
Parameters []VersionMenuParameterRequest `json:"parameters"`
|
||||
Children []VersionMenuRequest `json:"children"`
|
||||
}
|
||||
type VersionAPIRequest struct {
|
||||
Path string `json:"path"`
|
||||
|
||||
+9
-143
@@ -2,14 +2,10 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/xuri/excelize/v2"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/service/dto"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type ExportService struct{ uc *biz.ExportUsecase }
|
||||
@@ -31,8 +27,8 @@ 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) ([]map[string]any, int64, error) {
|
||||
return s.Templates(ctx, page, size, &biz.ExportTemplate{Name: name, TableName: tableName, TemplateID: templateID})
|
||||
func (s *ExportService) TemplatesFilter(ctx context.Context, page, size int, name, tableName, templateID string, start, end *time.Time) ([]map[string]any, 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))
|
||||
@@ -46,23 +42,23 @@ func exportDTO(v *biz.ExportTemplate) map[string]any {
|
||||
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}
|
||||
}
|
||||
func (s *ExportService) Create(ctx context.Context, v *biz.ExportTemplate) error {
|
||||
return s.uc.Repo().CreateExportTemplate(ctx, v)
|
||||
return s.uc.CreateExportTemplate(ctx, v)
|
||||
}
|
||||
func (s *ExportService) Update(ctx context.Context, v *biz.ExportTemplate) error {
|
||||
return s.uc.Repo().UpdateExportTemplate(ctx, v)
|
||||
return s.uc.UpdateExportTemplate(ctx, v)
|
||||
}
|
||||
func (s *ExportService) Delete(ctx context.Context, ids []uint) error {
|
||||
return s.uc.Repo().DeleteExportTemplates(ctx, ids)
|
||||
return s.uc.DeleteExportTemplates(ctx, ids)
|
||||
}
|
||||
func (s *ExportService) Template(ctx context.Context, id uint, tid string) (map[string]any, error) {
|
||||
v, err := s.uc.Repo().FindExportTemplate(ctx, id, tid)
|
||||
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) {
|
||||
items, total, err := s.uc.Repo().ListExportTemplates(ctx, page, size, q)
|
||||
items, total, err := s.uc.ListExportTemplates(ctx, page, size, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -72,133 +68,3 @@ func (s *ExportService) Templates(ctx context.Context, page, size int, q *biz.Ex
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func templateColumns(raw string) ([]string, map[string]string, error) {
|
||||
labels := map[string]string{}
|
||||
if err := json.Unmarshal([]byte(raw), &labels); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
keys := make([]string, 0, len(labels))
|
||||
for key := range labels {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys, labels, nil
|
||||
}
|
||||
func (s *ExportService) Export(ctx context.Context, tid string, params map[string]string) ([]byte, string, error) {
|
||||
template, err := s.uc.Repo().FindExportTemplate(ctx, 0, tid)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
rows, _, err := s.uc.Repo().QueryExport(ctx, template, params)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
keys, labels, err := templateColumns(template.TemplateInfo)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
file := excelize.NewFile()
|
||||
sheet := file.GetSheetName(0)
|
||||
for column, key := range keys {
|
||||
cell, _ := excelize.CoordinatesToCellName(column+1, 1)
|
||||
_ = file.SetCellValue(sheet, cell, labels[key])
|
||||
}
|
||||
for rowIndex, row := range rows {
|
||||
for column, key := range keys {
|
||||
cell, _ := excelize.CoordinatesToCellName(column+1, rowIndex+2)
|
||||
_ = file.SetCellValue(sheet, cell, row[key])
|
||||
}
|
||||
}
|
||||
buffer, err := file.WriteToBuffer()
|
||||
_ = file.Close()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return buffer.Bytes(), template.Name + ".xlsx", nil
|
||||
}
|
||||
func (s *ExportService) ExportBlankTemplate(ctx context.Context, tid string) ([]byte, string, error) {
|
||||
template, err := s.uc.Repo().FindExportTemplate(ctx, 0, tid)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
keys, labels, err := templateColumns(template.TemplateInfo)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
file := excelize.NewFile()
|
||||
sheet := file.GetSheetName(0)
|
||||
for column, key := range keys {
|
||||
cell, _ := excelize.CoordinatesToCellName(column+1, 1)
|
||||
_ = file.SetCellValue(sheet, cell, labels[key])
|
||||
}
|
||||
buffer, err := file.WriteToBuffer()
|
||||
_ = file.Close()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return buffer.Bytes(), template.Name + "_template.xlsx", nil
|
||||
}
|
||||
func (s *ExportService) Preview(ctx context.Context, tid string, params map[string]string) (string, error) {
|
||||
template, err := s.uc.Repo().FindExportTemplate(ctx, 0, tid)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, sql, err := s.uc.Repo().QueryExport(ctx, template, params)
|
||||
return sql, err
|
||||
}
|
||||
func (s *ExportService) Import(ctx context.Context, tid string, reader io.Reader) error {
|
||||
template, err := s.uc.Repo().FindExportTemplate(ctx, 0, tid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file, err := excelize.OpenReader(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
sheets := file.GetSheetList()
|
||||
if len(sheets) == 0 {
|
||||
return errors.New("工作簿没有工作表")
|
||||
}
|
||||
rows, err := file.GetRows(sheets[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(rows) < 2 {
|
||||
return nil
|
||||
}
|
||||
keys, labels, err := templateColumns(template.TemplateInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reverse := map[string]string{}
|
||||
for _, key := range keys {
|
||||
reverse[labels[key]] = key
|
||||
}
|
||||
headers := rows[0]
|
||||
items := make([]map[string]any, 0, len(rows)-1)
|
||||
for rowIndex, row := range rows[1:] {
|
||||
item := map[string]any{}
|
||||
empty := true
|
||||
for column, value := range row {
|
||||
if column >= len(headers) {
|
||||
continue
|
||||
}
|
||||
key := reverse[headers[column]]
|
||||
if key != "" {
|
||||
item[key] = value
|
||||
if value != "" {
|
||||
empty = false
|
||||
}
|
||||
}
|
||||
}
|
||||
if !empty {
|
||||
items = append(items, item)
|
||||
}
|
||||
_ = rowIndex
|
||||
}
|
||||
if err = s.uc.Repo().ImportExportRows(ctx, template, items); err != nil {
|
||||
return fmt.Errorf("导入失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
|
||||
func templateColumns(raw string) ([]string, map[string]string, error) {
|
||||
labels := map[string]string{}
|
||||
decoder := json.NewDecoder(strings.NewReader(raw))
|
||||
token, err := decoder.Token()
|
||||
if err != nil || token != json.Delim('{') {
|
||||
return nil, nil, err
|
||||
}
|
||||
keys := make([]string, 0)
|
||||
for decoder.More() {
|
||||
keyToken, tokenErr := decoder.Token()
|
||||
if tokenErr != nil {
|
||||
return nil, nil, tokenErr
|
||||
}
|
||||
key := keyToken.(string)
|
||||
var label string
|
||||
if tokenErr = decoder.Decode(&label); tokenErr != nil {
|
||||
return nil, nil, tokenErr
|
||||
}
|
||||
keys = append(keys, key)
|
||||
labels[key] = label
|
||||
}
|
||||
if _, err = decoder.Token(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return keys, labels, nil
|
||||
}
|
||||
func (s *ExportService) Export(ctx context.Context, tid string, params map[string]string) ([]byte, string, error) {
|
||||
template, err := s.uc.FindExportTemplate(ctx, 0, tid)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
rows, _, err := s.uc.QueryExport(ctx, template, params)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
keys, labels, err := templateColumns(template.TemplateInfo)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
file := excelize.NewFile()
|
||||
sheet := file.GetSheetName(0)
|
||||
for column, key := range keys {
|
||||
cell, _ := excelize.CoordinatesToCellName(column+1, 1)
|
||||
_ = file.SetCellValue(sheet, cell, labels[key])
|
||||
}
|
||||
for rowIndex, row := range rows {
|
||||
for column, key := range keys {
|
||||
cell, _ := excelize.CoordinatesToCellName(column+1, rowIndex+2)
|
||||
_ = file.SetCellValue(sheet, cell, row[key])
|
||||
}
|
||||
}
|
||||
buffer, err := file.WriteToBuffer()
|
||||
_ = file.Close()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return buffer.Bytes(), template.Name + ".xlsx", nil
|
||||
}
|
||||
func (s *ExportService) ExportBlankTemplate(ctx context.Context, tid string) ([]byte, string, error) {
|
||||
template, err := s.uc.FindExportTemplate(ctx, 0, tid)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
keys, labels, err := templateColumns(template.TemplateInfo)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
file := excelize.NewFile()
|
||||
sheet := file.GetSheetName(0)
|
||||
for column, key := range keys {
|
||||
cell, _ := excelize.CoordinatesToCellName(column+1, 1)
|
||||
_ = file.SetCellValue(sheet, cell, labels[key])
|
||||
}
|
||||
buffer, err := file.WriteToBuffer()
|
||||
_ = file.Close()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return buffer.Bytes(), template.Name + "_template.xlsx", nil
|
||||
}
|
||||
func (s *ExportService) Preview(ctx context.Context, tid string, params map[string]string) (string, error) {
|
||||
template, err := s.uc.FindExportTemplate(ctx, 0, tid)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return s.uc.PreviewExport(ctx, template, params)
|
||||
}
|
||||
func (s *ExportService) Import(ctx context.Context, tid string, reader io.Reader) error {
|
||||
template, err := s.uc.FindExportTemplate(ctx, 0, tid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file, err := excelize.OpenReader(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
sheets := file.GetSheetList()
|
||||
if len(sheets) == 0 {
|
||||
return errors.New("工作簿没有工作表")
|
||||
}
|
||||
rows, err := file.GetRows(sheets[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(rows) < 2 {
|
||||
return errors.New("Excel data is not enough.\nIt should contain title row and data")
|
||||
}
|
||||
keys, labels, err := templateColumns(template.TemplateInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reverse := map[string]string{}
|
||||
for _, key := range keys {
|
||||
reverse[labels[key]] = key
|
||||
}
|
||||
headers := rows[0]
|
||||
items := make([]map[string]any, 0, len(rows)-1)
|
||||
for _, row := range rows[1:] {
|
||||
item := map[string]any{}
|
||||
for column, value := range row {
|
||||
if column >= len(headers) {
|
||||
continue
|
||||
}
|
||||
key := reverse[headers[column]]
|
||||
if key != "" {
|
||||
item[key] = value
|
||||
}
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
if err = s.uc.ImportExportRows(ctx, template, items); err != nil {
|
||||
return fmt.Errorf("导入失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+10
-10
@@ -29,14 +29,14 @@ func (s *MediaService) Upload(ctx context.Context, userID uint, name, mime strin
|
||||
return mediaDTO(v), nil
|
||||
}
|
||||
func (s *MediaService) Media(ctx context.Context, id uint) (map[string]any, error) {
|
||||
v, err := s.uc.Repo().FindMedia(ctx, id)
|
||||
v, err := s.uc.FindMedia(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mediaDTO(v), nil
|
||||
}
|
||||
func (s *MediaService) MediaList(ctx context.Context, page, size int, keyword string, categoryID int, tag string, userID uint) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.Repo().ListMedia(ctx, page, size, keyword, categoryID, tag, userID)
|
||||
func (s *MediaService) MediaList(ctx context.Context, filter biz.MediaFilter) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.ListMedia(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -48,15 +48,15 @@ func (s *MediaService) MediaList(ctx context.Context, page, size int, keyword st
|
||||
}
|
||||
func (s *MediaService) Delete(ctx context.Context, id uint) error { return s.uc.Delete(ctx, id) }
|
||||
func (s *MediaService) Rename(ctx context.Context, id uint, name string) error {
|
||||
return s.uc.Repo().UpdateMediaName(ctx, id, name)
|
||||
return s.uc.UpdateMediaName(ctx, id, name)
|
||||
}
|
||||
func (s *MediaService) ImportURLs(ctx context.Context, items []*biz.MediaFile) error {
|
||||
return s.uc.Repo().CreateMediaBatch(ctx, items)
|
||||
return s.uc.CreateMediaBatch(ctx, items)
|
||||
}
|
||||
func (s *MediaService) ImportURLRequests(ctx context.Context, userID uint, values []dto.ImportMediaRequest) error {
|
||||
func (s *MediaService) ImportURLRequests(ctx context.Context, values []dto.ImportMediaRequest) error {
|
||||
items := make([]*biz.MediaFile, 0, len(values))
|
||||
for _, value := range values {
|
||||
items = append(items, &biz.MediaFile{Name: value.Name, CategoryID: value.ClassID, URL: value.URL, Tag: value.Tag, Key: value.Key, Size: value.Size, Mime: value.Mime, MD5: value.MD5, UserID: userID})
|
||||
items = append(items, &biz.MediaFile{Name: value.Name, CategoryID: value.ClassID, URL: value.URL, Tag: value.Tag, Key: value.Key, Size: value.Size, Mime: value.Mime, MD5: value.MD5, UserID: value.UserID})
|
||||
}
|
||||
return s.ImportURLs(ctx, items)
|
||||
}
|
||||
@@ -72,7 +72,7 @@ func (s *MediaService) Storage(ctx context.Context, prefix, cursor string, limit
|
||||
return out, next, more, nil
|
||||
}
|
||||
func (s *MediaService) Categories(ctx context.Context) ([]map[string]any, error) {
|
||||
items, err := s.uc.Repo().ListCategories(ctx)
|
||||
items, err := s.uc.ListCategories(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -83,11 +83,11 @@ func (s *MediaService) Categories(ctx context.Context) ([]map[string]any, error)
|
||||
return out, nil
|
||||
}
|
||||
func (s *MediaService) SaveCategory(ctx context.Context, v *biz.AttachmentCategory) error {
|
||||
return s.uc.Repo().SaveCategory(ctx, v)
|
||||
return s.uc.SaveCategory(ctx, v)
|
||||
}
|
||||
func (s *MediaService) SaveCategoryRequest(ctx context.Context, value *dto.CategoryRequest) error {
|
||||
return s.SaveCategory(ctx, &biz.AttachmentCategory{ID: value.ID, Name: value.Name, ParentID: value.PID})
|
||||
}
|
||||
func (s *MediaService) DeleteCategory(ctx context.Context, id uint) error {
|
||||
return s.uc.Repo().DeleteCategory(ctx, id)
|
||||
return s.uc.DeleteCategory(ctx, id)
|
||||
}
|
||||
|
||||
+44
-41
@@ -11,47 +11,6 @@ type MenuService struct{ uc *biz.MenuUsecase }
|
||||
|
||||
func NewMenuService(uc *biz.MenuUsecase) *MenuService { return &MenuService{uc: uc} }
|
||||
|
||||
func menuDomain(v *dto.MenuRequest) *biz.Menu {
|
||||
menu := &biz.Menu{ID: v.ID, ParentID: v.ParentID, Path: v.Path, Name: v.Name, Hidden: v.Hidden, Component: v.Component, Sort: v.Sort, ActiveName: v.Meta.ActiveName, KeepAlive: v.Meta.KeepAlive, DefaultMenu: v.Meta.DefaultMenu, Title: v.Meta.Title, Icon: v.Meta.Icon, CloseTab: v.Meta.CloseTab, TransitionType: v.Meta.TransitionType}
|
||||
menu.Buttons = make([]*biz.MenuButton, 0, len(v.MenuButtons))
|
||||
for _, button := range v.MenuButtons {
|
||||
menu.Buttons = append(menu.Buttons, &biz.MenuButton{ID: button.ID, Name: button.Name, Description: button.Description, MenuID: v.ID})
|
||||
}
|
||||
menu.Parameters = make([]*biz.MenuParameter, 0, len(v.Parameters))
|
||||
for _, parameter := range v.Parameters {
|
||||
menu.Parameters = append(menu.Parameters, &biz.MenuParameter{ID: parameter.ID, MenuID: v.ID, Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
|
||||
}
|
||||
return menu
|
||||
}
|
||||
|
||||
func menuResponse(menu *biz.Menu) *dto.MenuResponse {
|
||||
children := make([]*dto.MenuResponse, 0, len(menu.Children))
|
||||
for _, child := range menu.Children {
|
||||
children = append(children, menuResponse(child))
|
||||
}
|
||||
buttons := make([]dto.MenuButtonResponse, 0, len(menu.Buttons))
|
||||
buttonAuthorities := make(map[string]uint, len(menu.Buttons))
|
||||
for _, button := range menu.Buttons {
|
||||
buttons = append(buttons, dto.MenuButtonResponse{ID: button.ID, Name: button.Name, Description: button.Description, SysBaseMenuID: button.MenuID})
|
||||
if button.AuthorityID != 0 {
|
||||
buttonAuthorities[button.Name] = button.AuthorityID
|
||||
}
|
||||
}
|
||||
parameters := make([]dto.MenuParameterResponse, 0, len(menu.Parameters))
|
||||
for _, parameter := range menu.Parameters {
|
||||
parameters = append(parameters, dto.MenuParameterResponse{ID: parameter.ID, SysBaseMenuID: parameter.MenuID, Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
|
||||
}
|
||||
return &dto.MenuResponse{ID: menu.ID, ParentID: menu.ParentID, Path: menu.Path, Name: menu.Name, Hidden: menu.Hidden, Component: menu.Component, Sort: menu.Sort, Meta: dto.MenuMetaResponse{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, MenuButtons: buttons, Buttons: buttonAuthorities}
|
||||
}
|
||||
|
||||
func menuResponses(items []*biz.Menu) []*dto.MenuResponse {
|
||||
out := make([]*dto.MenuResponse, 0, len(items))
|
||||
for _, item := range items {
|
||||
out = append(out, menuResponse(item))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (s *MenuService) List(ctx context.Context) ([]*dto.MenuResponse, error) {
|
||||
items, err := s.uc.List(ctx)
|
||||
if err != nil {
|
||||
@@ -107,6 +66,50 @@ func (s *MenuService) AuthorityMenus(ctx context.Context, authorityID uint) ([]*
|
||||
func (s *MenuService) RoleIDs(ctx context.Context, menuID uint) ([]uint, error) {
|
||||
return s.uc.RoleIDs(ctx, menuID)
|
||||
}
|
||||
func (s *MenuService) DefaultRouterRoleIDs(ctx context.Context, menuID uint) ([]uint, error) {
|
||||
return s.uc.DefaultRouterRoleIDs(ctx, menuID)
|
||||
}
|
||||
func (s *MenuService) SetRoles(ctx context.Context, req *dto.SetMenuRolesRequest) error {
|
||||
return s.uc.SetRoles(ctx, req.MenuID, req.AuthorityIDs)
|
||||
}
|
||||
|
||||
func menuDomain(v *dto.MenuRequest) *biz.Menu {
|
||||
menu := &biz.Menu{ID: v.ID, ParentID: v.ParentID, Path: v.Path, Name: v.Name, Hidden: v.Hidden, Component: v.Component, Sort: v.Sort, ActiveName: v.Meta.ActiveName, KeepAlive: v.Meta.KeepAlive, DefaultMenu: v.Meta.DefaultMenu, Title: v.Meta.Title, Icon: v.Meta.Icon, CloseTab: v.Meta.CloseTab, TransitionType: v.Meta.TransitionType}
|
||||
menu.Buttons = make([]*biz.MenuButton, 0, len(v.MenuButtons))
|
||||
for _, button := range v.MenuButtons {
|
||||
menu.Buttons = append(menu.Buttons, &biz.MenuButton{ID: button.ID, Name: button.Name, Description: button.Description, MenuID: v.ID})
|
||||
}
|
||||
menu.Parameters = make([]*biz.MenuParameter, 0, len(v.Parameters))
|
||||
for _, parameter := range v.Parameters {
|
||||
menu.Parameters = append(menu.Parameters, &biz.MenuParameter{ID: parameter.ID, MenuID: v.ID, Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
|
||||
}
|
||||
return menu
|
||||
}
|
||||
|
||||
func menuResponse(menu *biz.Menu) *dto.MenuResponse {
|
||||
children := make([]*dto.MenuResponse, 0, len(menu.Children))
|
||||
for _, child := range menu.Children {
|
||||
children = append(children, menuResponse(child))
|
||||
}
|
||||
buttons := make([]dto.MenuButtonResponse, 0, len(menu.Buttons))
|
||||
buttonAuthorities := make(map[string]uint, len(menu.Buttons))
|
||||
for _, button := range menu.Buttons {
|
||||
buttons = append(buttons, dto.MenuButtonResponse{ID: button.ID, Name: button.Name, Description: button.Description, SysBaseMenuID: button.MenuID})
|
||||
if button.AuthorityID != 0 {
|
||||
buttonAuthorities[button.Name] = button.AuthorityID
|
||||
}
|
||||
}
|
||||
parameters := make([]dto.MenuParameterResponse, 0, len(menu.Parameters))
|
||||
for _, parameter := range menu.Parameters {
|
||||
parameters = append(parameters, dto.MenuParameterResponse{ID: parameter.ID, SysBaseMenuID: parameter.MenuID, Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
|
||||
}
|
||||
return &dto.MenuResponse{ID: menu.ID, ParentID: menu.ParentID, Path: menu.Path, Name: menu.Name, Hidden: menu.Hidden, Component: menu.Component, Sort: menu.Sort, Meta: dto.MenuMetaResponse{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, MenuButtons: buttons, Buttons: buttonAuthorities}
|
||||
}
|
||||
|
||||
func menuResponses(items []*biz.Menu) []*dto.MenuResponse {
|
||||
out := make([]*dto.MenuResponse, 0, len(items))
|
||||
for _, item := range items {
|
||||
out = append(out, menuResponse(item))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -18,7 +18,11 @@ func departmentResponse(value *biz.Department) *dto.DepartmentResponse {
|
||||
for _, child := range value.Children {
|
||||
children = append(children, departmentResponse(child))
|
||||
}
|
||||
return &dto.DepartmentResponse{ID: value.ID, CreatedAt: value.CreatedAt, UpdatedAt: value.UpdatedAt, DeletedAt: nil, Name: value.Name, ParentID: value.ParentID, Ancestors: value.Ancestors, Sort: value.Sort, LeaderID: value.LeaderID, Leader: nil, Status: value.Status, Children: children, NamePath: ""}
|
||||
var leader any
|
||||
if value.Leader != nil {
|
||||
leader = convertUser(value.Leader)
|
||||
}
|
||||
return &dto.DepartmentResponse{ID: value.ID, CreatedAt: value.CreatedAt, UpdatedAt: value.UpdatedAt, DeletedAt: nil, Name: value.Name, ParentID: value.ParentID, Ancestors: value.Ancestors, Sort: value.Sort, LeaderID: value.LeaderID, Leader: leader, Status: value.Status, Children: children, NamePath: value.NamePath}
|
||||
}
|
||||
|
||||
func departmentDomain(value *dto.DepartmentRequest) *biz.Department {
|
||||
@@ -69,7 +73,7 @@ func positionResponse(value *biz.Position) *dto.PositionResponse {
|
||||
return &dto.PositionResponse{ID: value.ID, CreatedAt: value.CreatedAt, UpdatedAt: value.UpdatedAt, DeletedAt: nil, Name: value.Name, Code: value.Code, Sort: value.Sort, Status: value.Status, Remark: value.Remark}
|
||||
}
|
||||
func (s *OrganizationService) Positions(ctx context.Context, req *dto.PositionListRequest) ([]*dto.PositionResponse, int64, error) {
|
||||
items, total, err := s.uc.Positions(ctx, req.Page, req.PageSize, &biz.Position{Name: req.Name, Code: req.Code})
|
||||
items, total, err := s.uc.Positions(ctx, req.Page, req.PageSize, &biz.PositionListFilter{Name: req.Name, Code: req.Code, Status: req.Status})
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/service/dto"
|
||||
@@ -16,14 +17,14 @@ func (s *SettingsService) CreateParameterRequest(ctx context.Context, req *dto.S
|
||||
func (s *SettingsService) UpdateParameterRequest(ctx context.Context, req *dto.SystemParameterRequest) error {
|
||||
return s.UpdateParameter(ctx, parameterDomain(req))
|
||||
}
|
||||
func (s *SettingsService) ParametersFilter(ctx context.Context, page, size int, name, key string) ([]map[string]any, int64, error) {
|
||||
return s.Parameters(ctx, page, size, &biz.SystemParameter{Name: name, Key: key})
|
||||
func (s *SettingsService) ParametersFilter(ctx context.Context, page, size int, name, key string, start, end *time.Time) ([]map[string]any, 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 (s *SettingsService) Parameters(ctx context.Context, page, size int, q *biz.SystemParameter) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.Repo().ListParameters(ctx, page, size, q)
|
||||
items, total, err := s.uc.ListParameters(ctx, page, size, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -34,18 +35,18 @@ func (s *SettingsService) Parameters(ctx context.Context, page, size int, q *biz
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *SettingsService) Parameter(ctx context.Context, id uint, key string) (map[string]any, error) {
|
||||
v, err := s.Repo().FindParameter(ctx, id, key)
|
||||
v, err := s.uc.FindParameter(ctx, id, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parameterDTO(v), nil
|
||||
}
|
||||
func (s *SettingsService) CreateParameter(ctx context.Context, v *biz.SystemParameter) error {
|
||||
return s.Repo().CreateParameter(ctx, v)
|
||||
return s.uc.CreateParameter(ctx, v)
|
||||
}
|
||||
func (s *SettingsService) UpdateParameter(ctx context.Context, v *biz.SystemParameter) error {
|
||||
return s.Repo().UpdateParameter(ctx, v)
|
||||
return s.uc.UpdateParameter(ctx, v)
|
||||
}
|
||||
func (s *SettingsService) DeleteParameters(ctx context.Context, ids []uint) error {
|
||||
return s.Repo().DeleteParameters(ctx, ids)
|
||||
return s.uc.DeleteParameters(ctx, ids)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/service/dto"
|
||||
@@ -19,7 +18,7 @@ func (s *SettingsService) CurrentSecurity(ctx context.Context) (*biz.SecurityCon
|
||||
return ©, nil
|
||||
}
|
||||
s.securityMu.RUnlock()
|
||||
value, err := s.Repo().SecurityConfig(ctx)
|
||||
value, err := s.uc.SecurityConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -37,10 +36,7 @@ func (s *SettingsService) Security(ctx context.Context) (map[string]any, error)
|
||||
return securityDTO(value), nil
|
||||
}
|
||||
func (s *SettingsService) SaveSecurity(ctx context.Context, value *biz.SecurityConfig) (map[string]any, error) {
|
||||
if value.KeyLong < 1 || value.ImgWidth < 1 || value.ImgHeight < 1 || value.PwdMinLength < 1 || value.LimitWindow < 1 || value.LimitCount < 1 || value.LockThreshold < 1 || value.LockDuration < 1 || value.PwdExpireDays < 1 {
|
||||
return nil, errors.New("安全配置数值必须大于0")
|
||||
}
|
||||
if err := s.Repo().SaveSecurityConfig(ctx, value); err != nil {
|
||||
if err := s.uc.UpdateSecurity(ctx, value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.securityMu.Lock()
|
||||
@@ -66,33 +62,5 @@ func (s *SettingsService) ValidatePassword(ctx context.Context, password string)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len([]rune(password)) < cfg.PwdMinLength {
|
||||
return errors.New("密码长度不足")
|
||||
}
|
||||
hasUpper, hasLower, hasDigit, hasSpecial := false, false, false, false
|
||||
for _, ch := range password {
|
||||
switch {
|
||||
case ch >= 'A' && ch <= 'Z':
|
||||
hasUpper = true
|
||||
case ch >= 'a' && ch <= 'z':
|
||||
hasLower = true
|
||||
case ch >= '0' && ch <= '9':
|
||||
hasDigit = true
|
||||
default:
|
||||
hasSpecial = true
|
||||
}
|
||||
}
|
||||
if cfg.PwdRequireUpper && !hasUpper {
|
||||
return errors.New("密码必须包含大写字母")
|
||||
}
|
||||
if cfg.PwdRequireLower && !hasLower {
|
||||
return errors.New("密码必须包含小写字母")
|
||||
}
|
||||
if cfg.PwdRequireDigit && !hasDigit {
|
||||
return errors.New("密码必须包含数字")
|
||||
}
|
||||
if cfg.PwdRequireSpecial && !hasSpecial {
|
||||
return errors.New("密码必须包含特殊字符")
|
||||
}
|
||||
return nil
|
||||
return s.uc.ValidatePassword(cfg, password)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/conf"
|
||||
@@ -9,12 +11,39 @@ import (
|
||||
|
||||
type SettingsService struct {
|
||||
uc *biz.SettingsUsecase
|
||||
config *conf.AdminBackend
|
||||
runtime *conf.Runtime
|
||||
securityMu sync.RWMutex
|
||||
securityCache *biz.SecurityConfig
|
||||
cache biz.Cache
|
||||
}
|
||||
|
||||
func NewSettingsService(uc *biz.SettingsUsecase, config *conf.AdminBackend) *SettingsService {
|
||||
return &SettingsService{uc: uc, config: config}
|
||||
func NewSettingsService(uc *biz.SettingsUsecase, runtime *conf.Runtime, cache biz.Cache) *SettingsService {
|
||||
return &SettingsService{uc: uc, runtime: runtime, cache: cache}
|
||||
}
|
||||
|
||||
func (s *SettingsService) UseMultipoint() bool {
|
||||
config := s.runtime.Admin()
|
||||
return config != nil && config.System != nil && config.System.UseMultipoint
|
||||
}
|
||||
|
||||
func activeTokenKey(username string) string { return "jwt:active:" + username }
|
||||
|
||||
func (s *SettingsService) ActiveTokenMatches(ctx context.Context, username, token string) (bool, error) {
|
||||
if !s.UseMultipoint() {
|
||||
return true, nil
|
||||
}
|
||||
active, ok, err := s.cache.Get(ctx, activeTokenKey(username))
|
||||
return ok && active == token, err
|
||||
}
|
||||
|
||||
func (s *SettingsService) RotateActiveToken(ctx context.Context, username, oldToken, newToken string, expiration time.Duration) error {
|
||||
if !s.UseMultipoint() {
|
||||
return nil
|
||||
}
|
||||
if oldToken != "" && oldToken != newToken {
|
||||
if err := s.BlacklistToken(ctx, oldToken); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return s.cache.Set(ctx, activeTokenKey(username), newToken, expiration)
|
||||
}
|
||||
func (s *SettingsService) Repo() biz.SettingsRepo { return s.uc.Repo() }
|
||||
|
||||
@@ -9,12 +9,12 @@ import (
|
||||
|
||||
type SystemService struct {
|
||||
uc *biz.SystemUsecase
|
||||
config *conf.AdminBackend
|
||||
runtime *conf.Runtime
|
||||
settings *SettingsService
|
||||
}
|
||||
|
||||
func NewSystemService(uc *biz.SystemUsecase, config *conf.AdminBackend, settings *SettingsService) *SystemService {
|
||||
return &SystemService{uc: uc, config: config, settings: settings}
|
||||
func NewSystemService(uc *biz.SystemUsecase, runtime *conf.Runtime, settings *SettingsService) *SystemService {
|
||||
return &SystemService{uc: uc, runtime: runtime, settings: settings}
|
||||
}
|
||||
|
||||
func (s *SystemService) IsInitialized(ctx context.Context) (bool, error) {
|
||||
|
||||
@@ -2,12 +2,15 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"kra/internal/conf"
|
||||
"kra/internal/service/dto"
|
||||
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
)
|
||||
|
||||
func (s *SystemService) PersistConfig(ctx context.Context) error { return s.uc.PersistConfig(ctx) }
|
||||
@@ -22,42 +25,84 @@ func (s *SystemService) PersistAdminConfig(ctx context.Context, value *conf.Admi
|
||||
|
||||
func (s *SystemService) ReloadConfig(ctx context.Context) error { return s.uc.ReloadConfig(ctx) }
|
||||
|
||||
func (s *SystemService) 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 *SystemService) SystemConfig() map[string]any {
|
||||
admin := map[string]any{"routerPrefix": ""}
|
||||
email := map[string]any{}
|
||||
if s.config == nil {
|
||||
return map[string]any{"config": map[string]any{"admin": admin, "email": email}}
|
||||
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"] = s.config.RouterPrefix
|
||||
if s.config.Jwt != nil {
|
||||
admin["jwt"] = map[string]any{"signingKey": "******", "expiresTime": s.config.Jwt.ExpiresTime.AsDuration().String(), "bufferTime": s.config.Jwt.BufferTime.AsDuration().String(), "issuer": s.config.Jwt.Issuer}
|
||||
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}
|
||||
}
|
||||
if s.config.Captcha != nil {
|
||||
admin["captcha"] = map[string]any{"keyLong": s.config.Captcha.KeyLong, "imgWidth": s.config.Captcha.ImgWidth, "imgHeight": s.config.Captcha.ImgHeight, "storeExpiration": s.config.Captcha.StoreExpiration.AsDuration().String()}
|
||||
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 s.config.Local != nil {
|
||||
admin["local"] = map[string]any{"storePath": s.config.Local.StorePath, "pathPrefix": s.config.Local.PathPrefix}
|
||||
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 s.config.Media != nil {
|
||||
admin["media"] = map[string]any{"sessionTtl": s.config.Media.SessionTtl}
|
||||
if config.Local != nil {
|
||||
admin["local"] = map[string]any{"storePath": config.Local.StorePath, "pathPrefix": config.Local.PathPrefix}
|
||||
}
|
||||
if s.config.Email != nil {
|
||||
email = map[string]any{"to": s.config.Email.To, "from": s.config.Email.From, "host": s.config.Email.Host, "secret": "******", "nickname": s.config.Email.Nickname, "port": s.config.Email.Port, "is-ssl": s.config.Email.IsSsl, "is-loginauth": s.config.Email.IsLoginAuth}
|
||||
if config.Media != nil {
|
||||
admin["media"] = map[string]any{"sessionTtl": config.Media.SessionTtl}
|
||||
}
|
||||
if s.config.Storage != nil {
|
||||
storage := proto.Clone(s.config.Storage).(*conf.AdminBackend_Storage)
|
||||
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
|
||||
}
|
||||
return map[string]any{"config": map[string]any{"admin": admin, "email": email}}
|
||||
data := s.runtime.Data()
|
||||
if data != nil {
|
||||
if data.Database != nil {
|
||||
data.Database.Password = "******"
|
||||
}
|
||||
for _, database := range data.DatabaseList {
|
||||
if database != nil {
|
||||
database.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 *SystemService) SaveSystemConfig(ctx context.Context, req *dto.SetSystemConfigRequest) error {
|
||||
if s.config == nil {
|
||||
config := s.runtime.Admin()
|
||||
if config == nil {
|
||||
return nil
|
||||
}
|
||||
next := proto.Clone(s.config).(*conf.AdminBackend)
|
||||
next := proto.Clone(config).(*conf.AdminBackend)
|
||||
data := s.runtime.Data()
|
||||
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
|
||||
if next.Jwt != nil {
|
||||
if req.Config.Admin.JWT.SigningKey != "" && req.Config.Admin.JWT.SigningKey != "******" {
|
||||
next.Jwt.SigningKey = req.Config.Admin.JWT.SigningKey
|
||||
@@ -65,6 +110,26 @@ func (s *SystemService) SaveSystemConfig(ctx context.Context, req *dto.SetSystem
|
||||
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)
|
||||
}
|
||||
}
|
||||
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 != "" {
|
||||
@@ -89,7 +154,37 @@ func (s *SystemService) SaveSystemConfig(ctx context.Context, req *dto.SetSystem
|
||||
next.Email.Secret = req.Config.Email.Secret
|
||||
}
|
||||
}
|
||||
return s.PersistAdminConfig(ctx, next)
|
||||
if req.Config.Data != nil {
|
||||
if data == nil {
|
||||
data = &conf.Data{}
|
||||
}
|
||||
if value := req.Config.Data.Database; value != nil {
|
||||
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}
|
||||
}
|
||||
if value := req.Config.Data.Redis; value != nil {
|
||||
redis := &conf.Data_Redis{Network: value.Network, Addr: value.Addr}
|
||||
if duration, parseErr := time.ParseDuration(value.ReadTimeout); parseErr == nil && duration >= 0 {
|
||||
redis.ReadTimeout = durationpb.New(duration)
|
||||
}
|
||||
if duration, parseErr := time.ParseDuration(value.WriteTimeout); parseErr == nil && duration >= 0 {
|
||||
redis.WriteTimeout = durationpb.New(duration)
|
||||
}
|
||||
data.Redis = redis
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
func objectStores(storage *conf.AdminBackend_Storage) []*conf.AdminBackend_ObjectStore {
|
||||
|
||||
+21
-164
@@ -1,31 +1,23 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/robfig/cron/v3"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/conf"
|
||||
"kra/internal/service/dto"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TaskService struct {
|
||||
uc *biz.TaskUsecase
|
||||
media *biz.MediaUsecase
|
||||
config *conf.AdminBackend
|
||||
uc *biz.TaskUsecase
|
||||
media *biz.MediaUsecase
|
||||
runtime *conf.Runtime
|
||||
}
|
||||
|
||||
func NewTaskService(uc *biz.TaskUsecase, media *biz.MediaUsecase, config *conf.AdminBackend) *TaskService {
|
||||
return &TaskService{uc: uc, media: media, config: config}
|
||||
func NewTaskService(uc *biz.TaskUsecase, media *biz.MediaUsecase, runtime *conf.Runtime) *TaskService {
|
||||
return &TaskService{uc: uc, media: media, runtime: runtime}
|
||||
}
|
||||
func taskDomain(v *dto.TaskRequest) *biz.TimedTask {
|
||||
return &biz.TimedTask{ID: v.ID, Name: v.Name, Description: v.Description, Spec: v.Spec, WithSeconds: v.WithSeconds, ExecutorType: v.ExecutorType, MethodName: v.MethodName, Params: v.Params, HTTPURL: v.HTTPURL, HTTPMethod: v.HTTPMethod, HTTPHeader: v.HTTPHeader, HTTPBody: v.HTTPBody, HTTPAllowPrivate: v.HTTPAllowPrivate, Enabled: v.Enabled}
|
||||
@@ -43,86 +35,26 @@ func (s *TaskService) UpdateRequest(ctx context.Context, req *dto.TaskRequest) e
|
||||
func (s *TaskService) ListRequest(ctx context.Context, page, size int, name, executorType string, enabled *bool, next map[uint]time.Time) ([]map[string]any, int64, error) {
|
||||
return s.Tasks(ctx, page, size, &biz.TimedTask{Name: name, ExecutorType: executorType, EnabledFilter: enabled}, next)
|
||||
}
|
||||
func (s *TaskService) Validate(v *biz.TimedTask) error {
|
||||
if v.Name == "" {
|
||||
return errors.New("任务名不能为空")
|
||||
}
|
||||
var err error
|
||||
if v.WithSeconds {
|
||||
_, err = cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor).Parse(v.Spec)
|
||||
} else {
|
||||
_, err = cron.ParseStandard(v.Spec)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("cron 表达式非法: %w", err)
|
||||
}
|
||||
switch v.ExecutorType {
|
||||
case "method":
|
||||
if v.MethodName != "ClearDB" && v.MethodName != "CleanStaleUploads" {
|
||||
return errors.New("方法未注册")
|
||||
}
|
||||
if len(v.Params) > 0 && !json.Valid(v.Params) {
|
||||
return errors.New("params 必须是合法 JSON")
|
||||
}
|
||||
case "http":
|
||||
parsed, parseErr := url.Parse(v.HTTPURL)
|
||||
if parseErr != nil || (parsed.Scheme != "http" && parsed.Scheme != "https") || parsed.Hostname() == "" || parsed.User != nil {
|
||||
return errors.New("httpUrl 必须是合法的 http/https 地址")
|
||||
}
|
||||
if len(v.HTTPHeader) > 0 {
|
||||
headers := map[string]string{}
|
||||
if json.Unmarshal(v.HTTPHeader, &headers) != nil {
|
||||
return errors.New("httpHeader 必须是 JSON 对象")
|
||||
}
|
||||
}
|
||||
default:
|
||||
return errors.New("executorType 必须为 method 或 http")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (s *TaskService) Create(ctx context.Context, v *biz.TimedTask) error {
|
||||
if err := s.Validate(v); err != nil {
|
||||
return err
|
||||
}
|
||||
exists, err := s.uc.Repo().TaskNameExists(ctx, v.Name, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
return fmt.Errorf("任务名 %s 已存在", v.Name)
|
||||
}
|
||||
return s.uc.Repo().CreateTask(ctx, v)
|
||||
return s.uc.Create(ctx, v)
|
||||
}
|
||||
func (s *TaskService) Update(ctx context.Context, v *biz.TimedTask) error {
|
||||
if v.ID == 0 {
|
||||
return errors.New("缺少任务 ID")
|
||||
}
|
||||
if err := s.Validate(v); err != nil {
|
||||
return err
|
||||
}
|
||||
exists, err := s.uc.Repo().TaskNameExists(ctx, v.Name, v.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
return fmt.Errorf("任务名 %s 已存在", v.Name)
|
||||
}
|
||||
return s.uc.Repo().UpdateTask(ctx, v)
|
||||
return s.uc.Update(ctx, v)
|
||||
}
|
||||
func (s *TaskService) Delete(ctx context.Context, id uint) error {
|
||||
return s.uc.Repo().DeleteTask(ctx, id)
|
||||
return s.uc.DeleteTask(ctx, id)
|
||||
}
|
||||
func (s *TaskService) Toggle(ctx context.Context, id uint, enabled bool) error {
|
||||
return s.uc.Repo().ToggleTask(ctx, id, enabled)
|
||||
return s.uc.ToggleTask(ctx, id, enabled)
|
||||
}
|
||||
func (s *TaskService) Task(ctx context.Context, id uint) (*biz.TimedTask, error) {
|
||||
return s.uc.Repo().FindTask(ctx, id)
|
||||
return s.uc.FindTask(ctx, id)
|
||||
}
|
||||
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 (s *TaskService) Tasks(ctx context.Context, page, size int, q *biz.TimedTask, next map[uint]time.Time) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.Repo().ListTasks(ctx, page, size, q)
|
||||
items, total, err := s.uc.ListTasks(ctx, page, size, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -137,8 +69,15 @@ func (s *TaskService) Tasks(ctx context.Context, page, size int, q *biz.TimedTas
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
|
||||
// ScheduledTasks is the typed internal contract used by the scheduler and
|
||||
// deliberately bypasses transport response maps.
|
||||
func (s *TaskService) ScheduledTasks(ctx context.Context) ([]*biz.TimedTask, error) {
|
||||
items, _, err := s.uc.ListTasks(ctx, 0, 0, nil)
|
||||
return items, err
|
||||
}
|
||||
func (s *TaskService) Logs(ctx context.Context, page, size int, taskID uint, status string) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.Repo().ListTaskLogs(ctx, page, size, taskID, status)
|
||||
items, total, err := s.uc.ListTaskLogs(ctx, page, size, taskID, status)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -148,85 +87,3 @@ func (s *TaskService) Logs(ctx context.Context, page, size int, taskID uint, sta
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func privateIP(ip net.IP) bool {
|
||||
return ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast() || ip.IsUnspecified()
|
||||
}
|
||||
func (s *TaskService) runHTTP(ctx context.Context, v *biz.TimedTask) (string, error) {
|
||||
parsed, _ := url.Parse(v.HTTPURL)
|
||||
if !v.HTTPAllowPrivate {
|
||||
ips, err := net.DefaultResolver.LookupIP(ctx, "ip", parsed.Hostname())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, ip := range ips {
|
||||
if privateIP(ip) {
|
||||
return "", errors.New("禁止访问内网或环回地址")
|
||||
}
|
||||
}
|
||||
}
|
||||
method := strings.ToUpper(v.HTTPMethod)
|
||||
if method == "" {
|
||||
method = http.MethodGet
|
||||
}
|
||||
request, err := http.NewRequestWithContext(ctx, method, v.HTTPURL, bytes.NewBufferString(v.HTTPBody))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
headers := map[string]string{}
|
||||
_ = json.Unmarshal(v.HTTPHeader, &headers)
|
||||
for key, value := range headers {
|
||||
request.Header.Set(key, value)
|
||||
}
|
||||
response, err := (&http.Client{Timeout: 30 * time.Second}).Do(request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
body, _ := io.ReadAll(io.LimitReader(response.Body, 4096))
|
||||
output := fmt.Sprintf("HTTP %d: %s", response.StatusCode, string(body))
|
||||
if response.StatusCode < 200 || response.StatusCode >= 300 {
|
||||
return output, fmt.Errorf("HTTP 状态码 %d", response.StatusCode)
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
func (s *TaskService) Run(ctx context.Context, v *biz.TimedTask, trigger string) (log *biz.TimedTaskLog) {
|
||||
started := time.Now()
|
||||
log = &biz.TimedTaskLog{TaskID: v.ID, TaskName: v.Name, TriggerType: trigger, StartedAt: started, Status: "success"}
|
||||
defer func() {
|
||||
if recovered := recover(); recovered != nil {
|
||||
log.Status = "fail"
|
||||
log.ErrorMsg = fmt.Sprint(recovered)
|
||||
}
|
||||
log.FinishedAt = time.Now()
|
||||
log.DurationMS = log.FinishedAt.Sub(started).Milliseconds()
|
||||
_ = s.uc.Repo().RecordTaskLog(context.Background(), log)
|
||||
}()
|
||||
runCtx, cancel := context.WithTimeout(ctx, 35*time.Second)
|
||||
defer cancel()
|
||||
var err error
|
||||
switch v.ExecutorType {
|
||||
case "method":
|
||||
switch v.MethodName {
|
||||
case "ClearDB":
|
||||
err = s.uc.Repo().CleanupLogs(runCtx)
|
||||
log.Output = "过期日志清理完成"
|
||||
case "CleanStaleUploads":
|
||||
ttl := 24
|
||||
if s.config != nil && s.config.Media != nil && s.config.Media.SessionTtl > 0 {
|
||||
ttl = int(s.config.Media.SessionTtl)
|
||||
}
|
||||
err = s.media.CleanupStale(runCtx, ttl)
|
||||
log.Output = "过期大文件上传会话清理完成"
|
||||
}
|
||||
case "http":
|
||||
log.Output, err = s.runHTTP(runCtx, v)
|
||||
}
|
||||
if err != nil {
|
||||
log.Status = "fail"
|
||||
log.ErrorMsg = err.Error()
|
||||
}
|
||||
return log
|
||||
}
|
||||
func (s *TaskService) RegisteredMethods() []map[string]any {
|
||||
return []map[string]any{{"name": "ClearDB", "description": "清理数据库过期日志(操作记录、JWT黑名单、定时任务日志)"}, {"name": "CleanStaleUploads", "description": "清理过期大文件上传会话"}}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
)
|
||||
|
||||
func privateIP(ip net.IP) bool {
|
||||
return ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast() || ip.IsUnspecified()
|
||||
}
|
||||
|
||||
func taskHTTPClient(allowPrivate bool) *http.Client {
|
||||
dialer := &net.Dialer{Timeout: 10 * time.Second, Control: func(_ string, address string, _ syscall.RawConn) error {
|
||||
if allowPrivate {
|
||||
return nil
|
||||
}
|
||||
host, _, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析拨号地址失败: %w", err)
|
||||
}
|
||||
ip := net.ParseIP(host)
|
||||
if ip == nil {
|
||||
return fmt.Errorf("非法拨号 IP: %s", host)
|
||||
}
|
||||
if privateIP(ip) {
|
||||
return fmt.Errorf("目标解析为内网/环回/链路本地地址, 已被 SSRF 防护拒绝(可在任务上开启\"允许内网\"豁免): %s", ip)
|
||||
}
|
||||
return nil
|
||||
}}
|
||||
return &http.Client{Timeout: 30 * time.Second, Transport: &http.Transport{Proxy: nil, DialContext: dialer.DialContext}}
|
||||
}
|
||||
|
||||
func (s *TaskService) runHTTP(ctx context.Context, v *biz.TimedTask) (string, error) {
|
||||
parsed, err := url.Parse(v.HTTPURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if parsed.Scheme != "http" && parsed.Scheme != "https" {
|
||||
return "", fmt.Errorf("仅允许 http/https, 实际为 %q", parsed.Scheme)
|
||||
}
|
||||
method := strings.ToUpper(v.HTTPMethod)
|
||||
if method == "" {
|
||||
method = http.MethodGet
|
||||
}
|
||||
request, err := http.NewRequestWithContext(ctx, method, v.HTTPURL, bytes.NewBufferString(v.HTTPBody))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
headers := map[string]string{}
|
||||
_ = json.Unmarshal(v.HTTPHeader, &headers)
|
||||
for key, value := range headers {
|
||||
request.Header.Set(key, value)
|
||||
}
|
||||
response, err := taskHTTPClient(v.HTTPAllowPrivate).Do(request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
body, _ := io.ReadAll(io.LimitReader(response.Body, 1<<20))
|
||||
output := fmt.Sprintf("HTTP %d: %s", response.StatusCode, string(body))
|
||||
if response.StatusCode < 200 || response.StatusCode >= 300 {
|
||||
return output, fmt.Errorf("非 2xx 响应: %d", response.StatusCode)
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
var errTaskTimeout = errors.New("任务执行超时")
|
||||
|
||||
func truncateTaskText(value string) string {
|
||||
const limit = 4000
|
||||
if len(value) <= limit {
|
||||
return value
|
||||
}
|
||||
return value[:limit] + "...(截断)"
|
||||
}
|
||||
|
||||
func (s *TaskService) runMethod(v *biz.TimedTask) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
defer func() {
|
||||
if recovered := recover(); recovered != nil {
|
||||
done <- fmt.Errorf("panic: %v", recovered)
|
||||
}
|
||||
}()
|
||||
var err error
|
||||
switch v.MethodName {
|
||||
case biz.TaskMethodClearDB:
|
||||
err = s.uc.CleanupLogs(ctx)
|
||||
case biz.TaskMethodUploads:
|
||||
ttl := 24
|
||||
config := s.runtime.Admin()
|
||||
if config != nil && config.Media != nil && config.Media.SessionTtl > 0 {
|
||||
ttl = int(config.Media.SessionTtl)
|
||||
}
|
||||
err = s.media.CleanupStale(ctx, ttl)
|
||||
}
|
||||
done <- err
|
||||
}()
|
||||
select {
|
||||
case err := <-done:
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
return errTaskTimeout
|
||||
}
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
return errTaskTimeout
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TaskService) Run(ctx context.Context, v *biz.TimedTask, trigger string) (log *biz.TimedTaskLog) {
|
||||
started := time.Now()
|
||||
log = &biz.TimedTaskLog{TaskID: v.ID, TaskName: v.Name, TriggerType: trigger, StartedAt: started, Status: "success"}
|
||||
defer func() {
|
||||
if recovered := recover(); recovered != nil {
|
||||
log.Status = "fail"
|
||||
log.ErrorMsg = truncateTaskText(fmt.Sprint(recovered))
|
||||
}
|
||||
log.Output = truncateTaskText(log.Output)
|
||||
log.FinishedAt = time.Now()
|
||||
log.DurationMS = log.FinishedAt.Sub(started).Milliseconds()
|
||||
logCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 3*time.Second)
|
||||
defer cancel()
|
||||
_ = s.uc.RecordTaskLog(logCtx, log)
|
||||
}()
|
||||
var err error
|
||||
switch v.ExecutorType {
|
||||
case biz.TaskExecutorMethod:
|
||||
err = s.runMethod(v)
|
||||
case biz.TaskExecutorHTTP:
|
||||
runCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
log.Output, err = s.runHTTP(runCtx, v)
|
||||
cancel()
|
||||
default:
|
||||
err = fmt.Errorf("未知执行器类型: %s", v.ExecutorType)
|
||||
}
|
||||
if err != nil {
|
||||
if errors.Is(err, errTaskTimeout) || errors.Is(err, context.DeadlineExceeded) {
|
||||
log.Status = "timeout"
|
||||
} else {
|
||||
log.Status = "fail"
|
||||
}
|
||||
log.ErrorMsg = truncateTaskText(err.Error())
|
||||
}
|
||||
return log
|
||||
}
|
||||
func (s *TaskService) RegisteredMethods() []map[string]any {
|
||||
methods := biz.RegisteredTaskMethods()
|
||||
out := make([]map[string]any, 0, len(methods))
|
||||
for _, method := range methods {
|
||||
out = append(out, map[string]any{"name": method.Name, "description": method.Description})
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPrivateIP(t *testing.T) {
|
||||
for _, value := range []string{"127.0.0.1", "10.0.0.1", "172.16.0.1", "192.168.1.1", "169.254.1.1", "::1"} {
|
||||
if !privateIP(net.ParseIP(value)) {
|
||||
t.Fatalf("expected %s to be private", value)
|
||||
}
|
||||
}
|
||||
if privateIP(net.ParseIP("8.8.8.8")) {
|
||||
t.Fatal("public IP was classified as private")
|
||||
}
|
||||
}
|
||||
+39
-106
@@ -7,26 +7,6 @@ import (
|
||||
"kra/internal/service/dto"
|
||||
)
|
||||
|
||||
func (s *SystemService) User(ctx context.Context, id uint) (map[string]any, error) {
|
||||
value, err := s.uc.User(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertUser(value), nil
|
||||
}
|
||||
|
||||
func (s *SystemService) Menus(ctx context.Context, authorityID uint) ([]map[string]any, 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
|
||||
}
|
||||
|
||||
type UserInput struct {
|
||||
ID uint
|
||||
Username, Password, NickName, HeaderImg, Phone, Email string
|
||||
@@ -48,6 +28,26 @@ func (s *SystemService) UpdateUserRequest(ctx context.Context, value *dto.UserRe
|
||||
return s.UpdateUser(ctx, userInput(value))
|
||||
}
|
||||
|
||||
func (s *SystemService) User(ctx context.Context, id uint) (map[string]any, error) {
|
||||
value, err := s.uc.User(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertUser(value), nil
|
||||
}
|
||||
|
||||
func (s *SystemService) Menus(ctx context.Context, authorityID uint) ([]map[string]any, 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
|
||||
}
|
||||
|
||||
func (s *SystemService) ListUsers(ctx context.Context, page, pageSize int, filter *biz.UserListFilter) ([]map[string]any, int64, error) {
|
||||
users, total, err := s.uc.ListUsers(ctx, page, pageSize, filter)
|
||||
if err != nil {
|
||||
@@ -59,51 +59,36 @@ func (s *SystemService) ListUsers(ctx context.Context, page, pageSize int, filte
|
||||
}
|
||||
return result, total, nil
|
||||
}
|
||||
|
||||
func (s *SystemService) Authorities(ctx context.Context) ([]map[string]any, error) {
|
||||
values, err := s.uc.Authorities(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]map[string]any, 0, len(values))
|
||||
for _, value := range values {
|
||||
result = append(result, convertAuthority(*value))
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *SystemService) CreateUser(ctx context.Context, input UserInput) (map[string]any, error) {
|
||||
if err := s.settings.ValidatePassword(ctx, input.Password); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
security, _ := s.settings.CurrentSecurity(ctx)
|
||||
mustChange := security != nil && security.ForceNewUserChangePassword
|
||||
if input.AuthorityID == 0 && len(input.AuthorityIDs) > 0 {
|
||||
input.AuthorityID = input.AuthorityIDs[0]
|
||||
}
|
||||
input.AuthorityIDs = includeAuthority(input.AuthorityIDs, input.AuthorityID)
|
||||
user, err := s.uc.CreateUser(ctx, &biz.User{Username: input.Username, Password: input.Password, NickName: input.NickName, HeaderImg: input.HeaderImg, AuthorityID: input.AuthorityID, Phone: input.Phone, Email: input.Email, Enable: input.Enable, MustChangePassword: mustChange})
|
||||
user, err := s.uc.CreateUser(ctx, &biz.User{Username: input.Username, Password: input.Password, NickName: input.NickName, HeaderImg: input.HeaderImg, AuthorityID: input.AuthorityID, Phone: input.Phone, Email: input.Email, Enable: input.Enable, MustChangePassword: mustChange}, input.AuthorityIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(input.AuthorityIDs) > 0 {
|
||||
if err = s.uc.SetUserAuthorities(ctx, user.ID, input.AuthorityIDs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err = s.uc.User(ctx, user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return convertUser(user), nil
|
||||
}
|
||||
func (s *SystemService) UpdateUser(ctx context.Context, input UserInput) error {
|
||||
input.AuthorityIDs = includeAuthority(input.AuthorityIDs, input.AuthorityID)
|
||||
if err := s.uc.UpdateUser(ctx, &biz.User{ID: input.ID, NickName: input.NickName, HeaderImg: input.HeaderImg, AuthorityID: input.AuthorityID, Phone: input.Phone, Email: input.Email, Enable: input.Enable}); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(input.AuthorityIDs) > 0 {
|
||||
return s.uc.SetUserAuthorities(ctx, input.ID, input.AuthorityIDs)
|
||||
}
|
||||
return nil
|
||||
return s.uc.UpdateUser(ctx, &biz.User{ID: input.ID, NickName: input.NickName, HeaderImg: input.HeaderImg, AuthorityID: input.AuthorityID, Phone: input.Phone, Email: input.Email, Enable: input.Enable}, input.AuthorityIDs)
|
||||
}
|
||||
func includeAuthority(ids []uint, primary uint) []uint {
|
||||
if primary == 0 {
|
||||
return ids
|
||||
}
|
||||
for _, id := range ids {
|
||||
if id == primary {
|
||||
return ids
|
||||
}
|
||||
}
|
||||
return append(ids, primary)
|
||||
func (s *SystemService) UpdateSelfUser(ctx context.Context, input UserInput) error {
|
||||
return s.uc.UpdateSelfUser(ctx, &biz.User{ID: input.ID, NickName: input.NickName, HeaderImg: input.HeaderImg, Phone: input.Phone, Email: input.Email, Enable: input.Enable})
|
||||
}
|
||||
func (s *SystemService) DeleteUser(ctx context.Context, id uint) error {
|
||||
return s.uc.DeleteUser(ctx, id)
|
||||
@@ -120,17 +105,7 @@ func (s *SystemService) ChangePassword(ctx context.Context, id uint, oldPassword
|
||||
}
|
||||
return s.uc.ChangePassword(ctx, id, oldPassword, newPassword)
|
||||
}
|
||||
func (s *SystemService) Authorities(ctx context.Context) ([]map[string]any, error) {
|
||||
values, err := s.uc.Authorities(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]map[string]any, 0, len(values))
|
||||
for _, value := range values {
|
||||
result = append(result, convertAuthority(*value))
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *SystemService) SetUserAuthorities(ctx context.Context, id uint, authorityIDs []uint) error {
|
||||
return s.uc.SetUserAuthorities(ctx, id, authorityIDs)
|
||||
}
|
||||
@@ -142,50 +117,8 @@ func (s *SystemService) SwitchAuthority(ctx context.Context, id, authorityID uin
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.issueLogin(user, authorityID)
|
||||
return s.issueLogin(ctx, user, authorityID)
|
||||
}
|
||||
func (s *SystemService) SetUserSetting(ctx context.Context, id uint, setting map[string]any) error {
|
||||
return s.uc.SetUserSetting(ctx, id, setting)
|
||||
}
|
||||
|
||||
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))
|
||||
for _, value := range user.Authorities {
|
||||
authorities = append(authorities, convertAuthority(value))
|
||||
}
|
||||
departments := make([]map[string]any, 0, len(user.Departments))
|
||||
for i := range user.Departments {
|
||||
departments = append(departments, departmentDTO(&user.Departments[i]))
|
||||
}
|
||||
positions := make([]map[string]any, 0, len(user.Positions))
|
||||
for i := range user.Positions {
|
||||
positions = append(positions, positionDTO(&user.Positions[i]))
|
||||
}
|
||||
department := map[string]any{}
|
||||
if user.Department != nil {
|
||||
department = departmentDTO(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}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package service
|
||||
|
||||
import "kra/internal/biz"
|
||||
|
||||
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 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))
|
||||
for _, value := range user.Authorities {
|
||||
authorities = append(authorities, convertAuthority(value))
|
||||
}
|
||||
departments := make([]map[string]any, 0, len(user.Departments))
|
||||
for i := range user.Departments {
|
||||
departments = append(departments, departmentDTO(&user.Departments[i]))
|
||||
}
|
||||
positions := make([]map[string]any, 0, len(user.Positions))
|
||||
for i := range user.Positions {
|
||||
positions = append(positions, positionDTO(&user.Positions[i]))
|
||||
}
|
||||
department := map[string]any{}
|
||||
if user.Department != nil {
|
||||
department = departmentDTO(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}
|
||||
}
|
||||
@@ -15,8 +15,8 @@ func NewVersionService(uc *biz.VersionUsecase) *VersionService { return &Version
|
||||
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 (s *VersionService) Versions(ctx context.Context, page, size int, name, code string) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.Repo().ListVersions(ctx, page, size, name, code)
|
||||
func (s *VersionService) Versions(ctx context.Context, page, size int, name, code string, createdAtRange []*time.Time) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.ListVersions(ctx, page, size, name, code, createdAtRange)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -27,24 +27,25 @@ func (s *VersionService) Versions(ctx context.Context, page, size int, name, cod
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *VersionService) Version(ctx context.Context, id uint) (map[string]any, error) {
|
||||
v, err := s.uc.Repo().FindVersion(ctx, id)
|
||||
v, err := s.uc.FindVersion(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return versionDTO(v), nil
|
||||
}
|
||||
func (s *VersionService) VersionData(ctx context.Context, id uint) (string, string, error) {
|
||||
v, err := s.uc.Repo().FindVersion(ctx, id)
|
||||
v, err := s.uc.FindVersion(ctx, id)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return v.Data, v.Code, nil
|
||||
}
|
||||
func (s *VersionService) DeleteVersions(ctx context.Context, ids []uint) error {
|
||||
return s.uc.Repo().DeleteVersions(ctx, ids)
|
||||
return s.uc.DeleteVersions(ctx, ids)
|
||||
}
|
||||
|
||||
func (s *VersionService) Export(ctx context.Context, name, code, description string, menuIDs, apiIDs, dictIDs []uint) error {
|
||||
bundle, err := s.uc.Repo().BuildVersionBundle(ctx, menuIDs, apiIDs, dictIDs)
|
||||
bundle, err := s.uc.BuildVersionBundle(ctx, menuIDs, apiIDs, dictIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -69,22 +70,28 @@ func (s *VersionService) Export(ctx context.Context, name, code, description str
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.uc.Repo().CreateVersion(ctx, &biz.Version{Name: name, Code: code, Description: description, Data: string(raw)})
|
||||
return s.uc.CreateVersion(ctx, &biz.Version{Name: name, Code: code, Description: description, Data: string(raw)})
|
||||
}
|
||||
func (s *VersionService) Import(ctx context.Context, bundle *biz.VersionBundle) error {
|
||||
return s.uc.Repo().ImportVersionBundle(ctx, bundle)
|
||||
return s.uc.ImportVersionBundle(ctx, bundle)
|
||||
}
|
||||
func versionMenuDomain(value dto.VersionMenuRequest) *biz.Menu {
|
||||
menu := &biz.Menu{Path: value.Path, Name: value.Name, Hidden: value.Hidden, Component: value.Component, Sort: value.Sort, ActiveName: value.Meta.ActiveName, KeepAlive: value.Meta.KeepAlive, DefaultMenu: value.Meta.DefaultMenu, Title: value.Meta.Title, Icon: value.Meta.Icon, CloseTab: value.Meta.CloseTab, TransitionType: value.Meta.TransitionType}
|
||||
for _, button := range value.MenuButtons {
|
||||
menu.Buttons = append(menu.Buttons, &biz.MenuButton{Name: button.Name, Description: button.Description})
|
||||
}
|
||||
for _, parameter := range value.Parameters {
|
||||
menu.Parameters = append(menu.Parameters, &biz.MenuParameter{Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
|
||||
}
|
||||
for _, child := range value.Children {
|
||||
menu.Children = append(menu.Children, versionMenuDomain(child))
|
||||
}
|
||||
return menu
|
||||
}
|
||||
func (s *VersionService) ImportRequest(ctx context.Context, req *dto.ImportVersionRequest) error {
|
||||
if req.Version.Name == "" || req.Version.Code == "" {
|
||||
return biz.ErrInvalidVersion
|
||||
}
|
||||
bundle := &biz.VersionBundle{Name: req.Version.Name, Code: req.Version.Code, Description: req.Version.Description, ExportTime: req.Version.ExportTime}
|
||||
for _, menu := range req.Menus {
|
||||
bundle.Menus = append(bundle.Menus, versionMenuDomain(menu))
|
||||
@@ -103,5 +110,15 @@ func (s *VersionService) ImportRequest(ctx context.Context, req *dto.ImportVersi
|
||||
}
|
||||
bundle.Dictionaries = append(bundle.Dictionaries, item)
|
||||
}
|
||||
return s.Import(ctx, bundle)
|
||||
if err := s.Import(ctx, bundle); err != nil {
|
||||
return err
|
||||
}
|
||||
raw, _ := json.Marshal(req)
|
||||
_ = s.uc.CreateVersion(ctx, &biz.Version{
|
||||
Name: req.Version.Name,
|
||||
Code: req.Version.Code + "_imported_" + time.Now().Format("20060102150405"),
|
||||
Description: "导入版本: " + req.Version.Description,
|
||||
Data: string(raw),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user