diff --git a/go.mod b/go.mod index 5ee5063c..63017877 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/go-sonic/sonic -go 1.21 +go 1.25.0 require ( github.com/Masterminds/sprig/v3 v3.2.3 @@ -31,7 +31,7 @@ require ( go.uber.org/dig v1.17.1 go.uber.org/fx v1.20.1 go.uber.org/zap v1.26.0 - golang.org/x/crypto v0.18.0 + golang.org/x/crypto v0.50.0 golang.org/x/image v0.15.0 gopkg.in/yaml.v2 v2.4.0 gorm.io/driver/mysql v1.5.2 @@ -105,12 +105,13 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.7.0 // indirect golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect - golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.20.0 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/mod v0.35.0 // indirect + golang.org/x/net v0.53.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.43.0 // indirect + golang.org/x/text v0.36.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.17.0 // indirect + golang.org/x/tools v0.44.0 // indirect google.golang.org/protobuf v1.32.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect diff --git a/go.sum b/go.sum index a5bec248..f93f502a 100644 --- a/go.sum +++ b/go.sum @@ -277,8 +277,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= +golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -286,8 +286,8 @@ golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= +golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -295,13 +295,13 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= +golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -315,15 +315,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= +golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= +golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -331,16 +331,16 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= -golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= +golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= diff --git a/service/impl/attachment.go b/service/impl/attachment.go index a1dd6288..c2781b63 100644 --- a/service/impl/attachment.go +++ b/service/impl/attachment.go @@ -143,7 +143,7 @@ func (a *attachmentServiceImpl) Upload(ctx context.Context, fileHeader *multipar record, err := attachmentDAL.WithContext(ctx).Where(attachmentDAL.Path.Eq(attachmentDTO.Path)).Take() if record != nil && err == nil { - return nil, xerr.BadParam.New("附件路径为 " + attachmentDTO.Path + " 已经存在"). + return nil, xerr.BadParam.New("附件路径为 %s 已经存在", attachmentDTO.Path). WithStatus(xerr.StatusBadRequest). WithMsg("附件路径为 " + attachmentDTO.Path + " 已经存在") } diff --git a/service/impl/category.go b/service/impl/category.go index 6dfff57a..3e9aed74 100644 --- a/service/impl/category.go +++ b/service/impl/category.go @@ -110,6 +110,10 @@ func (c categoryServiceImpl) buildTree(ctx context.Context, categories []*entity if err != nil { return nil, err } + validIDs := make(map[int32]struct{}, len(categories)) + for _, category := range categories { + validIDs[category.ID] = struct{}{} + } categoryVOs := make([]*vo.CategoryVO, len(categories)) for i, categoryDTO := range categoryDTOs { categoryVO := &vo.CategoryVO{ @@ -119,7 +123,9 @@ func (c categoryServiceImpl) buildTree(ctx context.Context, categories []*entity categoryVO.Password = "" } if categoryDTO.ParentID != 0 { - parentIDToCategoryMap[categoryDTO.ParentID] = append(parentIDToCategoryMap[categoryDTO.ParentID], categoryVO) + if _, ok := validIDs[categoryDTO.ParentID]; ok { + parentIDToCategoryMap[categoryDTO.ParentID] = append(parentIDToCategoryMap[categoryDTO.ParentID], categoryVO) + } } categoryVOs[i] = categoryVO } @@ -128,7 +134,8 @@ func (c categoryServiceImpl) buildTree(ctx context.Context, categories []*entity if ok { categoryVO.Children = children } - if categoryVO.ParentID == 0 { + _, parentExists := validIDs[categoryVO.ParentID] + if categoryVO.ParentID == 0 || !parentExists { categoryTree = append(categoryTree, categoryVO) } } diff --git a/service/impl/category_test.go b/service/impl/category_test.go new file mode 100644 index 00000000..39ff873d --- /dev/null +++ b/service/impl/category_test.go @@ -0,0 +1,117 @@ +package impl + +import ( + "context" + "testing" + "time" + + "github.com/go-sonic/sonic/consts" + "github.com/go-sonic/sonic/model/dto" + "github.com/go-sonic/sonic/model/entity" + "github.com/go-sonic/sonic/model/param" + "github.com/go-sonic/sonic/model/property" + "github.com/go-sonic/sonic/service" +) + +type mockOptionService struct{} + +func (m *mockOptionService) IsEnabledAbsolutePath(_ context.Context) (bool, error) { + return false, nil +} + +func (m *mockOptionService) GetBlogBaseURL(_ context.Context) (string, error) { + return "http://localhost", nil +} + +func (m *mockOptionService) GetOrByDefaultWithErr(_ context.Context, _ property.Property, defaultValue interface{}) (interface{}, error) { + return defaultValue, nil +} + +func (m *mockOptionService) GetPathSuffix(_ context.Context) (string, error) { + return "", nil +} + +func (m *mockOptionService) GetOrByDefault(_ context.Context, p property.Property) interface{} { + return p.DefaultValue +} + +func (m *mockOptionService) GetArchivePrefix(_ context.Context) (string, error) { return "", nil } +func (m *mockOptionService) Save(_ context.Context, _ map[string]string) error { return nil } +func (m *mockOptionService) ListAllOption(_ context.Context) ([]*dto.Option, error) { return nil, nil } +func (m *mockOptionService) GetLinksPrefix(_ context.Context) (string, error) { return "", nil } +func (m *mockOptionService) GetPhotoPrefix(_ context.Context) (string, error) { return "", nil } +func (m *mockOptionService) GetJournalPrefix(_ context.Context) (string, error) { return "", nil } +func (m *mockOptionService) GetActivatedThemeID(_ context.Context) (string, error) { return "", nil } +func (m *mockOptionService) GetPostPermalinkType(_ context.Context) (consts.PostPermalinkType, error) { + return "", nil +} +func (m *mockOptionService) GetSheetPermalinkType(_ context.Context) (consts.SheetPermaLinkType, error) { + return "", nil +} +func (m *mockOptionService) GetIndexPageSize(_ context.Context) int { return 10 } +func (m *mockOptionService) GetPostSort(_ context.Context) param.Sort { return param.Sort{} } +func (m *mockOptionService) GetPostSummaryLength(_ context.Context) int { return 150 } +func (m *mockOptionService) GetCategoryPrefix(_ context.Context) (string, error) { return "", nil } +func (m *mockOptionService) GetTagPrefix(_ context.Context) (string, error) { return "", nil } +func (m *mockOptionService) GetLinkPrefix(_ context.Context) (string, error) { return "", nil } +func (m *mockOptionService) GetSheetPrefix(_ context.Context) (string, error) { return "", nil } +func (m *mockOptionService) GetAttachmentType(_ context.Context) consts.AttachmentType { + return consts.AttachmentTypeLocal +} +func (m *mockOptionService) GetAdminURLPath(_ context.Context) (string, error) { return "", nil } + +var _ service.OptionService = (*mockOptionService)(nil) + +func newTestCategoryService() *categoryServiceImpl { + return &categoryServiceImpl{OptionService: &mockOptionService{}} +} + +func makeCategory(id, parentID int32) *entity.Category { + return &entity.Category{ + ID: id, + ParentID: parentID, + Name: "cat", + Slug: "cat", + CreateTime: time.Now(), + } +} + +func TestBuildTree_OrphanedParent(t *testing.T) { + svc := newTestCategoryService() + ctx := context.Background() + + // categories where parentID=999 does not exist in the list + categories := []*entity.Category{ + makeCategory(1, 0), + makeCategory(2, 999), + } + + tree, err := svc.buildTree(ctx, categories, false) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(tree) != 2 { + t.Fatalf("expected 2 root categories, got %d", len(tree)) + } +} + +func TestBuildTree_ValidParent(t *testing.T) { + svc := newTestCategoryService() + ctx := context.Background() + + categories := []*entity.Category{ + makeCategory(1, 0), + makeCategory(2, 1), + } + + tree, err := svc.buildTree(ctx, categories, false) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(tree) != 1 { + t.Fatalf("expected 1 root category, got %d", len(tree)) + } + if len(tree[0].Children) != 1 { + t.Fatalf("expected 1 child, got %d", len(tree[0].Children)) + } +}