优化结构
This commit is contained in:
@@ -19,16 +19,17 @@ func announcementDTO(item *system.Announcement) *dto.AnnouncementResponse {
|
||||
return &dto.AnnouncementResponse{ID: item.ID, CreatedAt: item.CreatedAt, UpdatedAt: item.UpdatedAt, DeletedAt: nil, Title: item.Title, Content: item.Content, UserID: item.UserID, Attachments: json.RawMessage(item.Attachments)}
|
||||
}
|
||||
|
||||
func announcementDO(in *dto.AnnouncementRequest) *system.Announcement {
|
||||
return &system.Announcement{ID: in.ID, Title: in.Title, Content: in.Content, UserID: in.UserID, Attachments: in.Attachments}
|
||||
func announcementDO(in *dto.AnnouncementRequest, userID uint) *system.Announcement {
|
||||
authorID := int(userID)
|
||||
return &system.Announcement{ID: in.ID, Title: in.Title, Content: in.Content, UserID: &authorID, Attachments: in.Attachments}
|
||||
}
|
||||
|
||||
func (s *AnnouncementService) Create(ctx context.Context, in *dto.AnnouncementRequest) error {
|
||||
return s.uc.Create(ctx, announcementDO(in))
|
||||
func (s *AnnouncementService) Create(ctx context.Context, userID uint, in *dto.AnnouncementRequest) error {
|
||||
return s.uc.Create(ctx, announcementDO(in, userID))
|
||||
}
|
||||
|
||||
func (s *AnnouncementService) Update(ctx context.Context, in *dto.AnnouncementRequest) error {
|
||||
return s.uc.Update(ctx, announcementDO(in))
|
||||
func (s *AnnouncementService) Update(ctx context.Context, userID uint, in *dto.AnnouncementRequest) error {
|
||||
return s.uc.Update(ctx, announcementDO(in, userID))
|
||||
}
|
||||
|
||||
func (s *AnnouncementService) Delete(ctx context.Context, id string) error {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"kra/internal/service/dto"
|
||||
)
|
||||
|
||||
func TestAnnouncementDomainUsesAuthenticatedUser(t *testing.T) {
|
||||
value := announcementDO(&dto.AnnouncementRequest{Title: "notice"}, 42)
|
||||
if value.UserID == nil || *value.UserID != 42 {
|
||||
t.Fatalf("announcement user = %#v, want 42", value.UserID)
|
||||
}
|
||||
}
|
||||
@@ -56,8 +56,8 @@ func (s *AuditService) DeleteOperations(ctx context.Context, ids []int) error {
|
||||
func (s *AuditRecorder) RecordLogin(ctx context.Context, v *system.LoginLog) error {
|
||||
return s.uc.RecordLogin(ctx, v)
|
||||
}
|
||||
func (s *AuditService) LoginsFilter(ctx context.Context, page, size int, username string, status bool) ([]*dto.LoginLogResponse, int64, error) {
|
||||
return s.Logins(ctx, page, size, &system.LoginLog{Username: username, Status: status, FilterByStatus: status})
|
||||
func (s *AuditService) LoginsFilter(ctx context.Context, page, size int, username string, status *bool) ([]*dto.LoginLogResponse, int64, error) {
|
||||
return s.Logins(ctx, page, size, &system.LoginLogFilter{Username: username, Status: status})
|
||||
}
|
||||
func loginDTO(v *system.LoginLog) *dto.LoginLogResponse {
|
||||
var user any = map[string]any{}
|
||||
@@ -66,7 +66,7 @@ func loginDTO(v *system.LoginLog) *dto.LoginLogResponse {
|
||||
}
|
||||
return &dto.LoginLogResponse{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, 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 *system.LoginLog) ([]*dto.LoginLogResponse, int64, error) {
|
||||
func (s *AuditService) Logins(ctx context.Context, page, size int, q *system.LoginLogFilter) ([]*dto.LoginLogResponse, int64, error) {
|
||||
items, total, err := s.uc.ListLogins(ctx, page, size, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
|
||||
@@ -24,13 +24,15 @@ func recordedErrorDomain(value *dto.ErrorRecordRequest) *system.ErrorRecord {
|
||||
return result
|
||||
}
|
||||
func mutatedErrorDomain(value *dto.ErrorRecordMutationRequest) *system.ErrorRecord {
|
||||
return &system.ErrorRecord{ID: value.ID, CreatedAt: value.CreatedAt, UpdatedAt: value.UpdatedAt, Form: value.Form, Info: value.Info, Level: value.Level, RequestID: value.RequestID, TraceID: value.TraceID, Solution: value.Solution, Status: value.Status}
|
||||
return &system.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 *AuditRecorder) CreateErrorRequest(ctx context.Context, req *dto.ErrorRecordRequest) error {
|
||||
return s.CreateError(ctx, recordedErrorDomain(req))
|
||||
}
|
||||
func (s *AuditRecorder) CreateErrorMutationRequest(ctx context.Context, req *dto.ErrorRecordMutationRequest) error {
|
||||
return s.CreateError(ctx, mutatedErrorDomain(req))
|
||||
value := mutatedErrorDomain(req)
|
||||
value.ID = 0
|
||||
return s.CreateError(ctx, value)
|
||||
}
|
||||
func (s *AuditService) UpdateErrorRequest(ctx context.Context, req *dto.ErrorRecordMutationRequest) error {
|
||||
return s.UpdateError(ctx, mutatedErrorDomain(req))
|
||||
|
||||
@@ -70,10 +70,10 @@ func (s *ExportService) ConsumeToken(ctx context.Context, token string, expectBl
|
||||
func exportTemplateDomain(value *dto.ExportTemplateRequest) *system.ExportTemplate {
|
||||
out := &system.ExportTemplate{ID: value.ID, DBName: value.DBName, Name: value.Name, TableName: value.TableName, TemplateID: value.TemplateID, TemplateInfo: value.TemplateInfo, SQL: value.SQL, ImportSQL: value.ImportSQL, Limit: value.Limit, Order: value.Order}
|
||||
for _, item := range value.Conditions {
|
||||
out.Conditions = append(out.Conditions, system.ExportCondition{ID: item.ID, CreatedAt: item.CreatedAt, UpdatedAt: item.UpdatedAt, TemplateID: item.TemplateID, From: item.From, Column: item.Column, Operator: item.Operator})
|
||||
out.Conditions = append(out.Conditions, system.ExportCondition{ID: item.ID, TemplateID: item.TemplateID, From: item.From, Column: item.Column, Operator: item.Operator})
|
||||
}
|
||||
for _, item := range value.Joins {
|
||||
out.Joins = append(out.Joins, system.ExportJoin{ID: item.ID, CreatedAt: item.CreatedAt, UpdatedAt: item.UpdatedAt, TemplateID: item.TemplateID, Join: item.Join, Table: item.Table, On: item.On})
|
||||
out.Joins = append(out.Joins, system.ExportJoin{ID: item.ID, TemplateID: item.TemplateID, Join: item.Join, Table: item.Table, On: item.On})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -69,15 +67,7 @@ func (s *ExportService) Export(ctx context.Context, tid string, params map[strin
|
||||
lookup = parts[1]
|
||||
}
|
||||
}
|
||||
value := fmt.Sprintf("%v", row[lookup])
|
||||
if timestamp, ok := row[lookup].(time.Time); ok {
|
||||
value = timestamp.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
if number, parseErr := strconv.ParseFloat(value, 64); parseErr == nil {
|
||||
_ = file.SetCellValue(sheet, cell, number)
|
||||
} else {
|
||||
_ = file.SetCellValue(sheet, cell, value)
|
||||
}
|
||||
_ = file.SetCellValue(sheet, cell, exportCellValue(row[lookup]))
|
||||
}
|
||||
}
|
||||
buffer, err := file.WriteToBuffer()
|
||||
@@ -87,6 +77,19 @@ func (s *ExportService) Export(ctx context.Context, tid string, params map[strin
|
||||
}
|
||||
return buffer.Bytes(), template.Name + ".xlsx", nil
|
||||
}
|
||||
|
||||
func exportCellValue(value any) any {
|
||||
switch typed := value.(type) {
|
||||
case time.Time:
|
||||
return typed.Format("2006-01-02 15:04:05")
|
||||
case []byte:
|
||||
return string(typed)
|
||||
case string:
|
||||
return typed
|
||||
default:
|
||||
return value
|
||||
}
|
||||
}
|
||||
func (s *ExportService) ExportBlankTemplate(ctx context.Context, tid string) ([]byte, string, error) {
|
||||
template, err := s.uc.FindExportTemplate(ctx, 0, tid)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package system
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestExportCellValuePreservesNumericText(t *testing.T) {
|
||||
value, ok := exportCellValue("007").(string)
|
||||
if !ok || value != "007" {
|
||||
t.Fatalf("exportCellValue() = %#v, want string 007", exportCellValue("007"))
|
||||
}
|
||||
}
|
||||
@@ -72,10 +72,10 @@ func (s *MediaService) Rename(ctx context.Context, id uint, name string) error {
|
||||
func (s *MediaService) ImportURLs(ctx context.Context, items []*system.MediaFile) error {
|
||||
return s.uc.CreateMediaBatch(ctx, items)
|
||||
}
|
||||
func (s *MediaService) ImportURLRequests(ctx context.Context, values []dto.ImportMediaRequest) error {
|
||||
func (s *MediaService) ImportURLRequests(ctx context.Context, userID uint, values []dto.ImportMediaRequest) error {
|
||||
items := make([]*system.MediaFile, 0, len(values))
|
||||
for _, value := range values {
|
||||
items = append(items, &system.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})
|
||||
items = append(items, &system.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})
|
||||
}
|
||||
return s.ImportURLs(ctx, items)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
bizsystem "kra/internal/biz/system"
|
||||
"kra/internal/service/dto"
|
||||
)
|
||||
|
||||
type mediaImportRepo struct {
|
||||
bizsystem.MediaRepo
|
||||
items []*bizsystem.MediaFile
|
||||
}
|
||||
|
||||
func (r *mediaImportRepo) CreateMediaBatch(_ context.Context, items []*bizsystem.MediaFile) error {
|
||||
r.items = items
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestImportURLRequestsUsesAuthenticatedUser(t *testing.T) {
|
||||
repo := &mediaImportRepo{}
|
||||
service := NewMediaService(bizsystem.NewMediaUsecase(repo, nil, nil), nil)
|
||||
if err := service.ImportURLRequests(context.Background(), 42, []dto.ImportMediaRequest{{Name: "image", URL: "https://example.test/image.png"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(repo.items) != 1 || repo.items[0].UserID != 42 {
|
||||
t.Fatalf("imported media = %#v, want authenticated user 42", repo.items)
|
||||
}
|
||||
}
|
||||
@@ -75,14 +75,14 @@ func (s *MenuService) SetRoles(ctx context.Context, req *dto.SetMenuRolesRequest
|
||||
}
|
||||
|
||||
func menuDomain(v *dto.MenuRequest) *system.Menu {
|
||||
menu := &system.Menu{CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, 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 := &system.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([]*system.MenuButton, 0, len(v.MenuButtons))
|
||||
for _, button := range v.MenuButtons {
|
||||
menu.Buttons = append(menu.Buttons, &system.MenuButton{CreatedAt: button.CreatedAt, UpdatedAt: button.UpdatedAt, ID: button.ID, Name: button.Name, Description: button.Description, MenuID: v.ID})
|
||||
menu.Buttons = append(menu.Buttons, &system.MenuButton{ID: button.ID, Name: button.Name, Description: button.Description, MenuID: v.ID})
|
||||
}
|
||||
menu.Parameters = make([]*system.MenuParameter, 0, len(v.Parameters))
|
||||
for _, parameter := range v.Parameters {
|
||||
menu.Parameters = append(menu.Parameters, &system.MenuParameter{CreatedAt: parameter.CreatedAt, UpdatedAt: parameter.UpdatedAt, ID: parameter.ID, MenuID: v.ID, Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
|
||||
menu.Parameters = append(menu.Parameters, &system.MenuParameter{ID: parameter.ID, MenuID: v.ID, Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
|
||||
}
|
||||
return menu
|
||||
}
|
||||
|
||||
@@ -4,32 +4,27 @@ import (
|
||||
"encoding/json"
|
||||
"kra/internal/biz/system"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"kra/internal/service/dto"
|
||||
)
|
||||
|
||||
func TestMenuDomainPreservesRelationIdentity(t *testing.T) {
|
||||
createdAt := time.Date(2025, time.March, 4, 5, 6, 7, 0, time.UTC)
|
||||
updatedAt := time.Date(2025, time.April, 5, 6, 7, 8, 0, time.UTC)
|
||||
menu := menuDomain(&dto.MenuRequest{
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
ID: 10,
|
||||
ID: 10,
|
||||
MenuButtons: []dto.MenuButtonRequest{{
|
||||
CreatedAt: createdAt, UpdatedAt: updatedAt, ID: 31, Name: "edit", Description: "编辑",
|
||||
ID: 31, Name: "edit", Description: "编辑",
|
||||
}},
|
||||
Parameters: []dto.MenuParameterRequest{{
|
||||
CreatedAt: createdAt, UpdatedAt: updatedAt, ID: 21, Type: "query", Key: "id", Value: "1",
|
||||
ID: 21, Type: "query", Key: "id", Value: "1",
|
||||
}},
|
||||
})
|
||||
if !menu.CreatedAt.Equal(createdAt) || !menu.UpdatedAt.Equal(updatedAt) {
|
||||
t.Fatalf("menu timestamps = (%v, %v), want (%v, %v)", menu.CreatedAt, menu.UpdatedAt, createdAt, updatedAt)
|
||||
if !menu.CreatedAt.IsZero() || !menu.UpdatedAt.IsZero() {
|
||||
t.Fatalf("menu timestamps = (%v, %v), want zero before persistence", menu.CreatedAt, menu.UpdatedAt)
|
||||
}
|
||||
if len(menu.Buttons) != 1 || menu.Buttons[0].ID != 31 || menu.Buttons[0].MenuID != 10 || !menu.Buttons[0].CreatedAt.Equal(createdAt) || !menu.Buttons[0].UpdatedAt.Equal(updatedAt) {
|
||||
if len(menu.Buttons) != 1 || menu.Buttons[0].ID != 31 || menu.Buttons[0].MenuID != 10 || !menu.Buttons[0].CreatedAt.IsZero() || !menu.Buttons[0].UpdatedAt.IsZero() {
|
||||
t.Fatalf("button conversion = %+v", menu.Buttons)
|
||||
}
|
||||
if len(menu.Parameters) != 1 || menu.Parameters[0].ID != 21 || menu.Parameters[0].MenuID != 10 || !menu.Parameters[0].CreatedAt.Equal(createdAt) || !menu.Parameters[0].UpdatedAt.Equal(updatedAt) {
|
||||
if len(menu.Parameters) != 1 || menu.Parameters[0].ID != 21 || menu.Parameters[0].MenuID != 10 || !menu.Parameters[0].CreatedAt.IsZero() || !menu.Parameters[0].UpdatedAt.IsZero() {
|
||||
t.Fatalf("parameter conversion = %+v", menu.Parameters)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ func NewParameterService(uc *system.ParameterUsecase) *ParameterService {
|
||||
}
|
||||
|
||||
func parameterDomain(value *dto.SystemParameterRequest) *system.SystemParameter {
|
||||
return &system.SystemParameter{ID: value.ID, CreatedAt: value.CreatedAt, UpdatedAt: value.UpdatedAt, Name: value.Name, Key: value.Key, Value: value.Value, Desc: value.Description}
|
||||
return &system.SystemParameter{ID: value.ID, Name: value.Name, Key: value.Key, Value: value.Value, Desc: value.Description}
|
||||
}
|
||||
func (s *ParameterService) CreateParameterRequest(ctx context.Context, req *dto.SystemParameterRequest) error {
|
||||
return s.CreateParameter(ctx, parameterDomain(req))
|
||||
|
||||
@@ -55,6 +55,13 @@ func (s *SecurityService) ValidatePassword(ctx context.Context, password string)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.ValidatePasswordWithConfig(cfg, password)
|
||||
}
|
||||
|
||||
func (s *SecurityService) ValidatePasswordWithConfig(cfg *system.SecurityConfig, password string) error {
|
||||
if cfg == nil {
|
||||
return errors.New("安全配置不可用")
|
||||
}
|
||||
if err := s.uc.ValidatePassword(cfg, password); err != nil {
|
||||
return &PasswordPolicyError{Err: err}
|
||||
}
|
||||
|
||||
@@ -29,10 +29,13 @@ func (s *UserService) ListUsersRequest(ctx context.Context, value *dto.UserListR
|
||||
}
|
||||
|
||||
func (s *UserService) CreateUserRequest(ctx context.Context, value *dto.UserRequest) (*dto.UserResponse, error) {
|
||||
if err := s.settings.ValidatePassword(ctx, value.Password); err != nil {
|
||||
security, err := s.settings.CurrentSecurity(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.settings.ValidatePasswordWithConfig(security, value.Password); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
security, _ := s.settings.CurrentSecurity(ctx)
|
||||
mustChange := security != nil && security.ForceNewUserChangePassword
|
||||
user, err := s.uc.CreateUser(ctx, &system.User{Username: value.Username, Password: value.Password, NickName: value.NickName, HeaderImg: value.HeaderImg, AuthorityID: value.AuthorityID, Phone: value.Phone, Email: value.Email, Enable: value.Enable, MustChangePassword: mustChange}, value.AuthorityIDs)
|
||||
if err != nil {
|
||||
|
||||
@@ -256,12 +256,17 @@ func (s *VersionService) ImportRequest(ctx context.Context, req *dto.ImportVersi
|
||||
if err := s.Import(ctx, bundle); err != nil {
|
||||
return err
|
||||
}
|
||||
raw, _ := json.Marshal(req)
|
||||
_ = s.uc.CreateVersion(ctx, &system.Version{
|
||||
raw, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return &system.VersionStageError{Stage: system.VersionStageJSON, Err: err}
|
||||
}
|
||||
if err = s.uc.CreateVersion(ctx, &system.Version{
|
||||
Name: versionPointer(req.Version.Name),
|
||||
Code: versionPointer(req.Version.Code + "_imported_" + time.Now().Format("20060102150405")),
|
||||
Description: versionPointer("导入版本: " + req.Version.Description),
|
||||
Data: versionPointer(string(raw)),
|
||||
})
|
||||
}); err != nil {
|
||||
return &system.VersionStageError{Stage: system.VersionStageSave, Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
bizsystem "kra/internal/biz/system"
|
||||
"kra/internal/service/dto"
|
||||
)
|
||||
|
||||
type versionImportRepo struct {
|
||||
bizsystem.VersionRepo
|
||||
saveErr error
|
||||
}
|
||||
|
||||
func (r *versionImportRepo) ImportVersionBundle(context.Context, *bizsystem.VersionBundle) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *versionImportRepo) CreateVersion(context.Context, *bizsystem.Version) error {
|
||||
return r.saveErr
|
||||
}
|
||||
|
||||
func TestImportRequestReturnsVersionAuditSaveFailure(t *testing.T) {
|
||||
repo := &versionImportRepo{saveErr: errors.New("database unavailable")}
|
||||
service := NewVersionService(bizsystem.NewVersionUsecase(repo))
|
||||
request := &dto.ImportVersionRequest{}
|
||||
request.Version.Name = "release"
|
||||
request.Version.Code = "v1"
|
||||
err := service.ImportRequest(context.Background(), request)
|
||||
var stageErr *bizsystem.VersionStageError
|
||||
if !errors.As(err, &stageErr) || stageErr.Stage != bizsystem.VersionStageSave {
|
||||
t.Fatalf("ImportRequest() error = %v, want save stage error", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user