This commit is contained in:
2026-08-17 02:12:17 +08:00
parent 560ae46b82
commit 6a05803e08
96 changed files with 2888 additions and 685 deletions
+3 -3
View File
@@ -39,15 +39,15 @@ func (s *AnnouncementService) Update(ctx context.Context, in AnnouncementInput)
return s.uc.Update(ctx, announcementDO(in))
}
func (s *AnnouncementService) Delete(ctx context.Context, id uint) error {
func (s *AnnouncementService) Delete(ctx context.Context, id string) error {
return s.uc.Delete(ctx, id)
}
func (s *AnnouncementService) DeleteByIDs(ctx context.Context, ids []uint) error {
func (s *AnnouncementService) DeleteByIDs(ctx context.Context, ids []string) error {
return s.uc.DeleteByIDs(ctx, ids)
}
func (s *AnnouncementService) Find(ctx context.Context, id uint) (*dto.AnnouncementResponse, error) {
func (s *AnnouncementService) Find(ctx context.Context, id string) (*dto.AnnouncementResponse, error) {
item, err := s.uc.Find(ctx, id)
if err != nil {
return nil, err
+5 -2
View File
@@ -71,7 +71,7 @@ func (s *APIService) Groups(ctx context.Context) ([]string, map[string]string, e
return nil, nil, err
}
seen := make(map[string]bool)
groups := make([]string, 0)
var groups []string
groupAPIMap := make(map[string]string)
for _, item := range items {
if !seen[item.APIGroup] {
@@ -126,7 +126,10 @@ func (s *APIService) PolicyPathResponses(ctx context.Context, authorityID uint)
if err != nil {
return nil, err
}
out := make([]dto.PolicyPathRequest, 0, len(items))
var out []dto.PolicyPathRequest
if len(items) > 0 {
out = make([]dto.PolicyPathRequest, 0, len(items))
}
for _, item := range items {
out = append(out, dto.PolicyPathRequest{Path: item.Path, Method: item.Method})
}
+8 -2
View File
@@ -28,7 +28,11 @@ func authorityDomain(req *dto.AuthorityRequest) *biz.Authority {
}
func authorityResponse(value *biz.Authority) *dto.AuthorityResponse {
return &dto.AuthorityResponse{CreatedAt: value.CreatedAt, UpdatedAt: value.UpdatedAt, DeletedAt: value.DeletedAt, AuthorityID: value.AuthorityID, AuthorityName: value.AuthorityName, ParentID: value.ParentID, Children: []*dto.AuthorityResponse{}, Menus: []any{}, DataScope: value.DataScope, DefaultRouter: value.DefaultRouter}
var menus []*dto.MenuResponse
if value.Menus != nil {
menus = menuResponses(value.Menus)
}
return &dto.AuthorityResponse{CreatedAt: value.CreatedAt, UpdatedAt: value.UpdatedAt, DeletedAt: value.DeletedAt, AuthorityID: value.AuthorityID, AuthorityName: value.AuthorityName, ParentID: value.ParentID, Children: nil, Menus: menus, DataScope: value.DataScope, DefaultRouter: value.DefaultRouter}
}
func (s *AuthorityService) Authorities(ctx context.Context) ([]*dto.AuthorityResponse, error) {
@@ -56,7 +60,9 @@ func (s *AuthorityService) CopyAuthorityRequest(ctx context.Context, req *dto.Co
if err := s.uc.CopyAuthority(ctx, req.OldAuthorityID, value); err != nil {
return nil, err
}
return authorityResponse(value), nil
result := authorityResponse(value)
result.Children = []*dto.AuthorityResponse{}
return result, nil
}
func (s *AuthorityService) UpdateAuthorityRequest(ctx context.Context, req *dto.AuthorityRequest) (*dto.AuthorityResponse, error) {
+6 -3
View File
@@ -14,9 +14,12 @@ func NewDepartmentService(uc *biz.DepartmentUsecase) *DepartmentService {
}
func departmentResponse(value *biz.Department) *dto.DepartmentResponse {
children := make([]*dto.DepartmentResponse, 0, len(value.Children))
for _, child := range value.Children {
children = append(children, departmentResponse(child))
var children []*dto.DepartmentResponse
if value.Children != nil {
children = make([]*dto.DepartmentResponse, 0, len(value.Children))
for _, child := range value.Children {
children = append(children, departmentResponse(child))
}
}
var leader any
if value.Leader != nil {
+16
View File
@@ -0,0 +1,16 @@
package service
import (
"testing"
"kra/internal/biz"
)
func TestDepartmentResponsePreservesNilAndEmptyChildren(t *testing.T) {
if got := departmentResponse(&biz.Department{}).Children; got != nil {
t.Fatalf("single/search department children = %#v, want nil", got)
}
if got := departmentResponse(&biz.Department{Children: []*biz.Department{}}).Children; got == nil || len(got) != 0 {
t.Fatalf("tree leaf children = %#v, want empty slice", got)
}
}
+1 -1
View File
@@ -66,7 +66,7 @@ func (s *DictionaryService) ExportDictionary(ctx context.Context, id uint) (*dto
if err != nil {
return nil, err
}
details := make([]*dto.DictionaryDetailExportResponse, 0, len(v.Details))
var details []*dto.DictionaryDetailExportResponse
for _, detail := range v.Details {
details = append(details, &dto.DictionaryDetailExportResponse{
Label: detail.Label, Value: detail.Value, Extend: detail.Extend,
+28
View File
@@ -0,0 +1,28 @@
package service
import (
"context"
"testing"
"kra/internal/biz"
)
type dictionaryExportRepo struct {
biz.DictionaryRepo
value *biz.Dictionary
}
func (r dictionaryExportRepo) ExportDictionary(context.Context, uint) (*biz.Dictionary, error) {
return r.value, nil
}
func TestExportDictionaryPreservesNilDetails(t *testing.T) {
service := NewDictionaryService(biz.NewDictionaryUsecase(dictionaryExportRepo{value: &biz.Dictionary{Name: "empty", Type: "empty"}}))
result, err := service.ExportDictionary(context.Background(), 1)
if err != nil {
t.Fatal(err)
}
if result.SysDictionaryDetails != nil {
t.Fatalf("export details = %#v, want nil", result.SysDictionaryDetails)
}
}
+38
View File
@@ -0,0 +1,38 @@
package dto
import (
"net/http/httptest"
"strings"
"testing"
"github.com/gin-gonic/gin"
)
func bindErrorMutation(t *testing.T, body string) (ErrorRecordMutationRequest, error) {
t.Helper()
context, _ := gin.CreateTestContext(httptest.NewRecorder())
context.Request = httptest.NewRequest("PUT", "/sysError/updateSysError", strings.NewReader(body))
context.Request.Header.Set("Content-Type", "application/json")
var request ErrorRecordMutationRequest
return request, context.ShouldBindJSON(&request)
}
func TestErrorRecordMutationAcceptsExplicitEmptyPointerFields(t *testing.T) {
request, err := bindErrorMutation(t, `{"ID":1,"form":"","info":"","solution":""}`)
if err != nil {
t.Fatal(err)
}
if request.Form == nil || *request.Form != "" || request.Info == nil || *request.Info != "" || request.Solution == nil || *request.Solution != "" {
t.Fatalf("explicit empty values lost: form=%#v info=%#v solution=%#v", request.Form, request.Info, request.Solution)
}
}
func TestErrorRecordMutationRequiresFormButKeepsOtherFieldsOmittable(t *testing.T) {
request, err := bindErrorMutation(t, `{"ID":1}`)
if err == nil {
t.Fatal("missing form unexpectedly accepted")
}
if request.Info != nil || request.Solution != nil {
t.Fatalf("omitted fields = (%#v, %#v), want nil", request.Info, request.Solution)
}
}
+4 -4
View File
@@ -20,12 +20,12 @@ type DeleteAuthorityRequest struct {
}
type SetRoleUsersRequest struct {
AuthorityID uint `json:"authorityId" form:"authorityId"`
UserIDs []uint `json:"userIds"`
UserIDs []uint `json:"userIds" form:"userIds"`
}
type SetDataScopeRequest struct {
AuthorityID uint `json:"authorityId" form:"authorityId"`
DataScope int `json:"dataScope"`
DepartmentIDs []uint `json:"deptIds"`
DataScope int `json:"dataScope" form:"dataScope"`
DepartmentIDs []uint `json:"deptIds" form:"deptIds"`
}
type AuthorityResponse struct {
@@ -36,7 +36,7 @@ type AuthorityResponse struct {
AuthorityName string `json:"authorityName"`
ParentID *uint `json:"parentId"`
Children []*AuthorityResponse `json:"children"`
Menus []any `json:"menus"`
Menus []*MenuResponse `json:"menus"`
DataScope int `json:"dataScope"`
DefaultRouter string `json:"defaultRouter"`
}
+3
View File
@@ -61,6 +61,9 @@ type IDRequest struct {
type IDsRequest struct {
IDs []uint `json:"ids"`
}
type MediaBatchDeleteRequest struct {
IDs []uint `json:"ids" binding:"required"`
}
type RenameMediaRequest struct {
ID uint `json:"ID"`
Name string `json:"name"`
+39 -17
View File
@@ -13,19 +13,27 @@ type MenuMetaRequest struct {
}
type MenuButtonRequest struct {
ID uint `json:"ID"`
Name string `json:"name"`
Description string `json:"desc"`
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
ID uint `json:"ID"`
Name string `json:"name"`
Description string `json:"desc"`
SysBaseMenuID uint `json:"sysBaseMenuID"`
}
type MenuParameterRequest struct {
ID uint `json:"ID"`
Type string `json:"type"`
Key string `json:"key"`
Value string `json:"value"`
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
ID uint `json:"ID"`
SysBaseMenuID uint `json:"SysBaseMenuID"`
Type string `json:"type"`
Key string `json:"key"`
Value string `json:"value"`
}
type MenuRequest struct {
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
ID uint `json:"ID"`
ParentID uint `json:"parentId"`
Path string `json:"path"`
@@ -39,20 +47,16 @@ type MenuRequest struct {
}
type DeleteMenuRequest struct {
ID uint `json:"ID"`
ID int `json:"id"`
}
type GetMenuRequest struct {
ID uint `json:"id"`
}
type AuthorityMenuItemRequest struct {
ID uint `json:"ID"`
ID int `json:"id"`
}
type SetAuthorityMenusRequest struct {
AuthorityID uint `json:"authorityId"`
Menus []AuthorityMenuItemRequest `json:"menus"`
AuthorityID uint `json:"authorityId"`
Menus []MenuRequest `json:"menus"`
}
type GetAuthorityMenusRequest struct {
@@ -108,14 +112,32 @@ type MenuResponse struct {
Children []*MenuResponse `json:"children"`
Parameters []MenuParameterResponse `json:"parameters"`
MenuButtons []MenuButtonResponse `json:"menuBtn"`
Authorities []*AuthorityResponse `json:"authoritys"`
}
type DynamicMenuResponse struct {
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
ID uint `json:"ID"`
MenuID uint `json:"menuId"`
ParentID uint `json:"parentId"`
Path string `json:"path"`
Name string `json:"name"`
Hidden bool `json:"hidden"`
Component string `json:"component"`
Sort int `json:"sort"`
Meta MenuMetaResponse `json:"meta"`
Children []*DynamicMenuResponse `json:"children"`
Parameters []MenuParameterResponse `json:"parameters"`
MenuButtons []MenuButtonResponse `json:"menuBtn"`
Buttons map[string]uint `json:"btns"`
Authorities []*AuthorityResponse `json:"authoritys"`
}
type AuthorityMenuResponse struct {
// The administration response embeds the base-menu fields, so menu fields are
// flat in the response (there is no sysBaseMenu envelope). Keep the
// authority ID internal, matching the compatible json:"-" field.
*MenuResponse
MenuID uint `json:"menuId"`
*DynamicMenuResponse
AuthorityID uint `json:"-"`
}
+3 -2
View File
@@ -1,8 +1,9 @@
package dto
type GetAuthorityButtonsRequest struct {
MenuID uint `json:"menuID"`
AuthorityID uint `json:"authorityId"`
MenuID uint `json:"menuID"`
AuthorityID uint `json:"authorityId"`
Selected []uint `json:"selected"`
}
type SetAuthorityButtonsRequest struct {
+7 -5
View File
@@ -31,11 +31,13 @@ type DictionaryDetailsByParentRequest struct {
IncludeChildren bool `json:"includeChildren" form:"includeChildren"`
}
type SystemParameterRequest struct {
ID uint `json:"ID" form:"ID"`
Name string `json:"name" form:"name" binding:"required"`
Key string `json:"key" form:"key" binding:"required"`
Value string `json:"value" form:"value" binding:"required"`
Description string `json:"desc" form:"desc"`
ID uint `json:"ID" form:"ID"`
CreatedAt time.Time `json:"CreatedAt" form:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt" form:"UpdatedAt"`
Name string `json:"name" form:"name" binding:"required"`
Key string `json:"key" form:"key" binding:"required"`
Value string `json:"value" form:"value" binding:"required"`
Description string `json:"desc" form:"desc"`
}
type SystemParameterSearchRequest struct {
+20 -19
View File
@@ -75,25 +75,26 @@ type LoginLogRequest struct {
}
type UserResponse struct {
ID uint `json:"ID"`
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
DeletedAt any `json:"-"`
UUID string `json:"uuid"`
Username string `json:"userName"`
NickName string `json:"nickName"`
HeaderImg string `json:"headerImg"`
AuthorityID uint `json:"authorityId"`
Authority *AuthorityResponse `json:"authority"`
Authorities []*AuthorityResponse `json:"authorities"`
DeptID uint `json:"deptId"`
Department any `json:"dept"`
Departments []*DepartmentResponse `json:"departments"`
Positions []*PositionResponse `json:"positions"`
Phone string `json:"phone"`
Email string `json:"email"`
Enable int `json:"enable"`
OriginSetting map[string]any `json:"originSetting"`
ID uint `json:"ID"`
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
DeletedAt any `json:"-"`
UUID string `json:"uuid"`
Username string `json:"userName"`
NickName string `json:"nickName"`
HeaderImg string `json:"headerImg"`
AuthorityID uint `json:"authorityId"`
Authority *AuthorityResponse `json:"authority"`
Authorities []*AuthorityResponse `json:"authorities"`
DeptID uint `json:"deptId"`
Department any `json:"dept"`
Departments []*DepartmentResponse `json:"departments"`
Positions []*PositionResponse `json:"positions"`
Phone string `json:"phone"`
Email string `json:"email"`
Enable int `json:"enable"`
OriginSetting map[string]any `json:"originSetting"`
PasswordUpdatedAt *time.Time `json:"passwordUpdatedAt"`
}
type ServerInfoResponse struct {
+4 -4
View File
@@ -7,10 +7,10 @@ type VersionResponse struct {
CreatedAt time.Time `json:"CreatedAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
DeletedAt any `json:"-"`
VersionName string `json:"versionName"`
VersionCode string `json:"versionCode"`
Description string `json:"description"`
VersionData string `json:"versionData"`
VersionName *string `json:"versionName"`
VersionCode *string `json:"versionCode"`
Description *string `json:"description"`
VersionData *string `json:"versionData"`
}
type VersionSearchRequest struct {
+64 -19
View File
@@ -56,9 +56,10 @@ func (s *MenuService) AuthorityMenus(ctx context.Context, authorityID uint) ([]*
if err != nil {
return nil, err
}
out := make([]*dto.AuthorityMenuResponse, 0, len(items))
var out []*dto.AuthorityMenuResponse
for _, item := range items {
out = append(out, &dto.AuthorityMenuResponse{MenuResponse: menuResponse(item), MenuID: item.ID, AuthorityID: authorityID})
item.MenuID = item.ID
out = append(out, &dto.AuthorityMenuResponse{DynamicMenuResponse: dynamicMenuResponse(item), AuthorityID: authorityID})
}
return out, nil
}
@@ -74,42 +75,86 @@ func (s *MenuService) SetRoles(ctx context.Context, req *dto.SetMenuRolesRequest
}
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 := &biz.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.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.Buttons = append(menu.Buttons, &biz.MenuButton{CreatedAt: button.CreatedAt, UpdatedAt: button.UpdatedAt, 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})
menu.Parameters = append(menu.Parameters, &biz.MenuParameter{CreatedAt: parameter.CreatedAt, UpdatedAt: parameter.UpdatedAt, 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{CreatedAt: button.CreatedAt, UpdatedAt: button.UpdatedAt, ID: button.ID, Name: button.Name, Description: button.Description, SysBaseMenuID: button.MenuID})
if button.AuthorityID != 0 {
buttonAuthorities[button.Name] = button.AuthorityID
var children []*dto.MenuResponse
if menu.Children != nil {
children = make([]*dto.MenuResponse, 0, len(menu.Children))
for _, child := range menu.Children {
children = append(children, menuResponse(child))
}
}
parameters := make([]dto.MenuParameterResponse, 0, len(menu.Parameters))
for _, parameter := range menu.Parameters {
parameters = append(parameters, dto.MenuParameterResponse{CreatedAt: parameter.CreatedAt, UpdatedAt: parameter.UpdatedAt, ID: parameter.ID, SysBaseMenuID: parameter.MenuID, Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
var buttons []dto.MenuButtonResponse
if menu.Buttons != nil {
buttons = make([]dto.MenuButtonResponse, 0, len(menu.Buttons))
for _, button := range menu.Buttons {
buttons = append(buttons, dto.MenuButtonResponse{CreatedAt: button.CreatedAt, UpdatedAt: button.UpdatedAt, ID: button.ID, Name: button.Name, Description: button.Description, SysBaseMenuID: button.MenuID})
}
}
return &dto.MenuResponse{CreatedAt: menu.CreatedAt, UpdatedAt: menu.UpdatedAt, ID: menu.ID, MenuID: menu.MenuID, 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}
var parameters []dto.MenuParameterResponse
if menu.Parameters != nil {
parameters = make([]dto.MenuParameterResponse, 0, len(menu.Parameters))
for _, parameter := range menu.Parameters {
parameters = append(parameters, dto.MenuParameterResponse{CreatedAt: parameter.CreatedAt, UpdatedAt: parameter.UpdatedAt, ID: parameter.ID, SysBaseMenuID: parameter.MenuID, Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
}
}
return &dto.MenuResponse{CreatedAt: menu.CreatedAt, UpdatedAt: menu.UpdatedAt, ID: menu.ID, MenuID: menu.MenuID, 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}
}
func dynamicMenuResponse(menu *biz.Menu) *dto.DynamicMenuResponse {
var children []*dto.DynamicMenuResponse
if menu.Children != nil {
children = make([]*dto.DynamicMenuResponse, 0, len(menu.Children))
for _, child := range menu.Children {
children = append(children, dynamicMenuResponse(child))
}
}
var parameters []dto.MenuParameterResponse
if menu.Parameters != nil {
parameters = make([]dto.MenuParameterResponse, 0, len(menu.Parameters))
for _, parameter := range menu.Parameters {
parameters = append(parameters, dto.MenuParameterResponse{CreatedAt: parameter.CreatedAt, UpdatedAt: parameter.UpdatedAt, ID: parameter.ID, SysBaseMenuID: parameter.MenuID, Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
}
}
var buttonAuthorities map[string]uint
for _, button := range menu.Buttons {
if button.AuthorityID == 0 {
continue
}
if buttonAuthorities == nil {
buttonAuthorities = make(map[string]uint)
}
buttonAuthorities[button.Name] = button.AuthorityID
}
return &dto.DynamicMenuResponse{CreatedAt: menu.CreatedAt, UpdatedAt: menu.UpdatedAt, ID: menu.ID, MenuID: menu.MenuID, 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: nil, Buttons: buttonAuthorities}
}
func menuResponses(items []*biz.Menu) []*dto.MenuResponse {
if items == nil {
return nil
}
out := make([]*dto.MenuResponse, 0, len(items))
for _, item := range items {
out = append(out, menuResponse(item))
}
return out
}
func dynamicMenuResponses(items []*biz.Menu) []*dto.DynamicMenuResponse {
out := make([]*dto.DynamicMenuResponse, 0, len(items))
for _, item := range items {
out = append(out, dynamicMenuResponse(item))
}
return out
}
+63
View File
@@ -0,0 +1,63 @@
package service
import (
"encoding/json"
"testing"
"time"
"kra/internal/biz"
"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,
MenuButtons: []dto.MenuButtonRequest{{
CreatedAt: createdAt, UpdatedAt: updatedAt, ID: 31, Name: "edit", Description: "编辑",
}},
Parameters: []dto.MenuParameterRequest{{
CreatedAt: createdAt, UpdatedAt: updatedAt, 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 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) {
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) {
t.Fatalf("parameter conversion = %+v", menu.Parameters)
}
}
func TestMenuResponseEmptyCollectionContracts(t *testing.T) {
if got := menuResponses(nil); got != nil {
t.Fatalf("base menu top-level nil = %#v, want nil", got)
}
if got := dynamicMenuResponses(nil); got == nil || len(got) != 0 {
t.Fatalf("dynamic menu top-level empty = %#v, want non-nil empty slice", got)
}
base := menuResponse(&biz.Menu{Children: nil, Buttons: []*biz.MenuButton{}, Parameters: []*biz.MenuParameter{}})
if base.Children != nil {
t.Fatalf("leaf children = %#v, want nil", base.Children)
}
if base.MenuButtons == nil || base.Parameters == nil {
t.Fatalf("preloaded empty relations = (%#v, %#v), want empty slices", base.MenuButtons, base.Parameters)
}
encoded, err := json.Marshal(base)
if err != nil {
t.Fatal(err)
}
var payload map[string]any
if err := json.Unmarshal(encoded, &payload); err != nil {
t.Fatal(err)
}
if value, ok := payload["authoritys"]; !ok || value != nil {
t.Fatalf("authoritys JSON = %#v (present=%v), want explicit null", value, ok)
}
}
+11 -4
View File
@@ -15,7 +15,7 @@ func NewParameterService(uc *biz.ParameterUsecase) *ParameterService {
}
func parameterDomain(value *dto.SystemParameterRequest) *biz.SystemParameter {
return &biz.SystemParameter{ID: value.ID, Name: value.Name, Key: value.Key, Value: value.Value, Desc: value.Description}
return &biz.SystemParameter{ID: value.ID, CreatedAt: value.CreatedAt, UpdatedAt: value.UpdatedAt, 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))
@@ -40,8 +40,15 @@ func (s *ParameterService) Parameters(ctx context.Context, page, size int, q *bi
}
return out, total, nil
}
func (s *ParameterService) Parameter(ctx context.Context, id uint, key string) (*dto.SystemParameterResponse, error) {
v, err := s.uc.FindParameter(ctx, id, key)
func (s *ParameterService) ParameterByID(ctx context.Context, id string) (*dto.SystemParameterResponse, error) {
v, err := s.uc.FindParameterByID(ctx, id)
if err != nil {
return nil, err
}
return parameterDTO(v), nil
}
func (s *ParameterService) ParameterByKey(ctx context.Context, key string) (*dto.SystemParameterResponse, error) {
v, err := s.uc.FindParameterByKey(ctx, key)
if err != nil {
return nil, err
}
@@ -53,6 +60,6 @@ func (s *ParameterService) CreateParameter(ctx context.Context, v *biz.SystemPar
func (s *ParameterService) UpdateParameter(ctx context.Context, v *biz.SystemParameter) error {
return s.uc.UpdateParameter(ctx, v)
}
func (s *ParameterService) DeleteParameters(ctx context.Context, ids []uint) error {
func (s *ParameterService) DeleteParameters(ctx context.Context, ids []string) error {
return s.uc.DeleteParameters(ctx, ids)
}
+1 -1
View File
@@ -20,6 +20,6 @@ func (s *PermissionService) SetSelectedButtons(ctx context.Context, authorityID,
return s.uc.SetSelectedButtons(ctx, authorityID, menuID, ids)
}
func (s *PermissionService) CanRemoveButton(ctx context.Context, id uint) (bool, error) {
func (s *PermissionService) CanRemoveButton(ctx context.Context, id string) (bool, error) {
return s.uc.CanRemoveButton(ctx, id)
}
+1 -1
View File
@@ -25,7 +25,7 @@ func (s *SecurityService) CurrentSecurity(ctx context.Context) (*biz.SecurityCon
return s.uc.Current(ctx)
}
func (s *SecurityService) Security(ctx context.Context) (*dto.SecurityConfigResponse, error) {
value, err := s.CurrentSecurity(ctx)
value, err := s.uc.Security(ctx)
if err != nil {
return nil, err
}
+10 -2
View File
@@ -45,12 +45,20 @@ func (s *UserService) User(ctx context.Context, id uint) (*dto.UserResponse, err
return convertUser(value), nil
}
func (s *UserService) Menus(ctx context.Context, authorityID uint) ([]*dto.MenuResponse, error) {
func (s *UserService) UserByUUID(ctx context.Context, uuid string) (*dto.UserResponse, error) {
value, err := s.uc.UserByUUID(ctx, uuid)
if err != nil {
return nil, err
}
return convertUser(value), nil
}
func (s *UserService) Menus(ctx context.Context, authorityID uint) ([]*dto.DynamicMenuResponse, error) {
menus, err := s.uc.Menus(ctx, authorityID)
if err != nil {
return nil, err
}
return menuResponses(menus), nil
return dynamicMenuResponses(menus), nil
}
func (s *UserService) ListUsers(ctx context.Context, page, pageSize int, filter *biz.UserListFilter) ([]*dto.UserResponse, int64, error) {
+27 -12
View File
@@ -6,24 +6,39 @@ import (
)
func convertAuthority(value biz.Authority) *dto.AuthorityResponse {
return &dto.AuthorityResponse{CreatedAt: value.CreatedAt, UpdatedAt: value.UpdatedAt, DeletedAt: value.DeletedAt, AuthorityID: value.AuthorityID, AuthorityName: value.AuthorityName, ParentID: value.ParentID, Children: []*dto.AuthorityResponse{}, Menus: []any{}, DataScope: value.DataScope, DefaultRouter: value.DefaultRouter}
var menus []*dto.MenuResponse
if value.Menus != nil {
menus = menuResponses(value.Menus)
}
return &dto.AuthorityResponse{CreatedAt: value.CreatedAt, UpdatedAt: value.UpdatedAt, DeletedAt: value.DeletedAt, AuthorityID: value.AuthorityID, AuthorityName: value.AuthorityName, ParentID: value.ParentID, Children: nil, Menus: menus, DataScope: value.DataScope, DefaultRouter: value.DefaultRouter}
}
func convertUser(user *biz.User) *dto.UserResponse {
authorities := make([]*dto.AuthorityResponse, 0, len(user.Authorities))
for _, value := range user.Authorities {
authorities = append(authorities, convertAuthority(value))
var authorities []*dto.AuthorityResponse
if user.Authorities != nil {
authorities = make([]*dto.AuthorityResponse, 0, len(user.Authorities))
for _, value := range user.Authorities {
authorities = append(authorities, convertAuthority(value))
}
}
departments := make([]*dto.DepartmentResponse, 0, len(user.Departments))
for i := range user.Departments {
departments = append(departments, departmentResponse(&user.Departments[i]))
var departments []*dto.DepartmentResponse
if user.Departments != nil {
departments = make([]*dto.DepartmentResponse, 0, len(user.Departments))
for i := range user.Departments {
departments = append(departments, departmentResponse(&user.Departments[i]))
}
}
positions := make([]*dto.PositionResponse, 0, len(user.Positions))
for i := range user.Positions {
positions = append(positions, positionResponse(&user.Positions[i]))
var positions []*dto.PositionResponse
if user.Positions != nil {
positions = make([]*dto.PositionResponse, 0, len(user.Positions))
for i := range user.Positions {
positions = append(positions, positionResponse(&user.Positions[i]))
}
}
var department any = map[string]any{}
// The primary department is represented by a zero-value object when it is
// absent, preserving the response shape expected by the administration UI.
var department any = dto.DepartmentResponse{}
if user.Department != nil {
department = departmentResponse(user.Department)
}
return &dto.UserResponse{ID: user.ID, CreatedAt: user.CreatedAt, UpdatedAt: user.UpdatedAt, DeletedAt: nil, UUID: user.UUID, Username: user.Username, NickName: user.NickName, HeaderImg: user.HeaderImg, AuthorityID: user.AuthorityID, Authority: convertAuthority(user.Authority), Authorities: authorities, DeptID: user.DeptID, Department: department, Departments: departments, Positions: positions, Phone: user.Phone, Email: user.Email, Enable: user.Enable, OriginSetting: user.OriginSetting}
return &dto.UserResponse{ID: user.ID, CreatedAt: user.CreatedAt, UpdatedAt: user.UpdatedAt, DeletedAt: nil, UUID: user.UUID, Username: user.Username, NickName: user.NickName, HeaderImg: user.HeaderImg, AuthorityID: user.AuthorityID, Authority: convertAuthority(user.Authority), Authorities: authorities, DeptID: user.DeptID, Department: department, Departments: departments, Positions: positions, Phone: user.Phone, Email: user.Email, Enable: user.Enable, OriginSetting: user.OriginSetting, PasswordUpdatedAt: user.PasswordUpdatedAt}
}
+18 -9
View File
@@ -132,6 +132,15 @@ func exportMenu(value *biz.Menu) *versionExportMenu {
func versionDTO(v *biz.Version) *dto.VersionResponse {
return &dto.VersionResponse{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, DeletedAt: nil, VersionName: v.Name, VersionCode: v.Code, Description: v.Description, VersionData: v.Data}
}
func versionValue(value *string) string {
if value == nil {
return ""
}
return *value
}
func versionPointer(value string) *string { return &value }
func (s *VersionService) Versions(ctx context.Context, page, size int, name, code string, createdAtRange []*time.Time) ([]*dto.VersionResponse, int64, error) {
items, total, err := s.uc.ListVersions(ctx, page, size, name, code, createdAtRange)
if err != nil {
@@ -155,11 +164,11 @@ func (s *VersionService) VersionData(ctx context.Context, id uint) (string, stri
if err != nil {
return "", "", err
}
if v.Data != "" {
return v.Data, v.Code, nil
if v.Data != nil && *v.Data != "" {
return *v.Data, versionValue(v.Code), nil
}
payload := versionExportDocument{
Version: versionExportInfo{Name: v.Name, Code: v.Code, Description: v.Description, ExportTime: v.CreatedAt.Format("2006-01-02 15:04:05")},
Version: versionExportInfo{Name: versionValue(v.Name), Code: versionValue(v.Code), Description: versionValue(v.Description), ExportTime: v.CreatedAt.Format("2006-01-02 15:04:05")},
Menus: []*versionExportMenu{},
APIs: []versionExportAPI{},
}
@@ -167,7 +176,7 @@ func (s *VersionService) VersionData(ctx context.Context, id uint) (string, stri
if marshalErr != nil {
return "", "", marshalErr
}
return string(raw), v.Code, nil
return string(raw), versionValue(v.Code), nil
}
func (s *VersionService) DeleteVersions(ctx context.Context, ids []uint) error {
return s.uc.DeleteVersions(ctx, ids)
@@ -206,7 +215,7 @@ func (s *VersionService) Export(ctx context.Context, name, code, description str
if err != nil {
return &biz.VersionStageError{Stage: biz.VersionStageJSON, Err: err}
}
if err = s.uc.CreateVersion(ctx, &biz.Version{Name: name, Code: code, Description: description, Data: string(raw)}); err != nil {
if err = s.uc.CreateVersion(ctx, &biz.Version{Name: versionPointer(name), Code: versionPointer(code), Description: versionPointer(description), Data: versionPointer(string(raw))}); err != nil {
return &biz.VersionStageError{Stage: biz.VersionStageSave, Err: err}
}
return nil
@@ -250,10 +259,10 @@ func (s *VersionService) ImportRequest(ctx context.Context, req *dto.ImportVersi
}
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),
Name: versionPointer(req.Version.Name),
Code: versionPointer(req.Version.Code + "_imported_" + time.Now().Format("20060102150405")),
Description: versionPointer("导入版本: " + req.Version.Description),
Data: versionPointer(string(raw)),
})
return nil
}