From d52b1c96d6ae52d9076398b1b92ba838af4a431e Mon Sep 17 00:00:00 2001 From: xing Date: Sat, 14 Jan 2023 21:12:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84postmeta?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- helper/func.go | 13 +++ helper/func_test.go | 53 +++++++++ helper/map.go | 32 ++++++ helper/map_test.go | 165 +++++++++++++++++++++++++++++ internal/pkg/cache/postmeta.go | 43 -------- internal/pkg/dao/postmeta.go | 54 ++++++---- internal/pkg/dao/posts.go | 4 +- internal/pkg/models/wp_postmeta.go | 64 +++++++++++ internal/pkg/models/wp_posts.go | 17 +-- 9 files changed, 371 insertions(+), 74 deletions(-) create mode 100644 helper/map.go create mode 100644 helper/map_test.go diff --git a/helper/func.go b/helper/func.go index cf63c43..b5e918e 100644 --- a/helper/func.go +++ b/helper/func.go @@ -66,6 +66,19 @@ func SimpleSort[T any](arr []T, fn func(i, j T) bool) { return } +func SimpleSortR[T any](arr []T, fn func(i, j T) bool) (r []T) { + r = make([]T, 0, len(arr)) + for _, t := range arr { + r = append(r, t) + } + slice := anyArr[T]{ + data: r, + fn: fn, + } + sort.Sort(slice) + return +} + func Min[T IntNumber | ~float64 | ~float32](a ...T) T { min := a[0] for _, t := range a { diff --git a/helper/func_test.go b/helper/func_test.go index 76f2b21..3fd7bdd 100644 --- a/helper/func_test.go +++ b/helper/func_test.go @@ -280,3 +280,56 @@ func TestNumberToString(t *testing.T) { }) } } + +func TestSimpleSortR(t *testing.T) { + type xy struct { + x int + y int + } + type args[T any] struct { + arr []T + fn func(i, j T) bool + } + type testCase[T any] struct { + name string + args args[T] + wantR []T + } + tests := []testCase[xy]{ + { + name: "t1", + args: args[xy]{ + arr: []xy{ + {1, 2}, + {3, 4}, + {1, 3}, + {2, 1}, + {1, 6}, + }, + fn: func(i, j xy) bool { + if i.x < j.x { + return true + } + if i.x == j.x && i.y > i.y { + return true + } + return false + }, + }, + wantR: []xy{ + {1, 2}, + {1, 3}, + {1, 6}, + {2, 1}, + {3, 4}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotR := SimpleSortR[xy](tt.args.arr, tt.args.fn); !reflect.DeepEqual(gotR, tt.wantR) { + t.Errorf("SimpleSortR() = %v, want %v", gotR, tt.wantR) + } + }) + } +} diff --git a/helper/map.go b/helper/map.go new file mode 100644 index 0000000..3bd8439 --- /dev/null +++ b/helper/map.go @@ -0,0 +1,32 @@ +package helper + +import "encoding/json" + +func MapToStruct[T any](m map[string]any) (r T, err error) { + str, err := json.Marshal(m) + if err != nil { + return + } + err = json.Unmarshal(str, &r) + return +} + +func StructToMap[T any](s T) (r map[string]any, err error) { + marshal, err := json.Marshal(s) + if err != nil { + return + } + r = make(map[string]any) + err = json.Unmarshal(marshal, &r) + return +} + +func MapToSlice[T any, K comparable, V any](m map[K]V, fn func(K, V) (T, bool)) (r []T) { + for k, v := range m { + vv, ok := fn(k, v) + if ok { + r = append(r, vv) + } + } + return +} diff --git a/helper/map_test.go b/helper/map_test.go new file mode 100644 index 0000000..2bb9edc --- /dev/null +++ b/helper/map_test.go @@ -0,0 +1,165 @@ +package helper + +import ( + "reflect" + "testing" +) + +type Addr struct { + PostalCode int + Country string +} +type Me struct { + Name string + Age int + Admin bool + Hobbies []string + Address Addr + Null any +} + +func TestMapToStruct(t *testing.T) { + type args struct { + m map[string]any + } + + type testCase[T any] struct { + name string + args args + wantR T + wantErr bool + } + tests := []testCase[Me]{ + { + name: "t1", + args: args{ + m: map[string]any{ + "name": "noknow", + "Age": 2, + "Admin": true, + "Hobbies": []string{"IT", "Travel"}, + "Address": map[string]any{ + "PostalCode": 1111, + "Country": "Japan", + }, + "Null": nil, + }, + }, + wantR: Me{ + Name: "noknow", + Age: 2, + Admin: true, + Hobbies: []string{"IT", "Travel"}, + Address: Addr{ + PostalCode: 1111, + Country: "Japan", + }, + Null: nil, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotR, err := MapToStruct[Me](tt.args.m) + if (err != nil) != tt.wantErr { + t.Errorf("MapToStruct() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(gotR, tt.wantR) { + t.Errorf("MapToStruct() gotR = %v, want %v", gotR, tt.wantR) + } + }) + } +} + +func TestStructToMap(t *testing.T) { + type args[T any] struct { + s T + } + type testCase[T any] struct { + name string + args args[T] + wantR map[string]any + wantErr bool + } + tests := []testCase[Me]{ + { + name: "t1", + args: args[Me]{ + s: Me{ + Name: "noknow", + Age: 2, + Admin: true, + Hobbies: []string{"IT", "Travel"}, + Address: Addr{ + PostalCode: 1111, + Country: "Japan", + }, + Null: nil, + }, + }, + wantR: map[string]any{ + "Name": "noknow", + "Age": 2, + "Admin": true, + "Hobbies": []string{"IT", "Travel"}, + "Address": map[string]any{ + "PostalCode": 1111, + "Country": "Japan", + }, + "Null": nil, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotR, err := StructToMap[Me](tt.args.s) + if (err != nil) != tt.wantErr { + t.Errorf("StructToMap() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(gotR, tt.wantR) { + t.Errorf("StructToMap() gotR = %v, want %v", gotR, tt.wantR) + } + }) + } +} + +func TestMapToSlice(t *testing.T) { + type args[K comparable, V any, T any] struct { + m map[K]V + fn func(K, V) (T, bool) + } + type testCase[K comparable, V any, T any] struct { + name string + args args[K, V, T] + wantR []T + } + tests := []testCase[string, int, int]{ + { + name: "t1", + args: args[string, int, int]{ + m: map[string]int{ + "0": 0, + "1": 1, + "2": 2, + "3": 3, + }, + fn: func(k string, v int) (int, bool) { + if v > 2 { + return v, true + } + return 0, false + }, + }, + wantR: []int{3}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotR := MapToSlice(tt.args.m, tt.args.fn); !reflect.DeepEqual(gotR, tt.wantR) { + t.Errorf("MapToSlice() = %v, want %v", gotR, tt.wantR) + } + }) + } +} diff --git a/internal/pkg/cache/postmeta.go b/internal/pkg/cache/postmeta.go index e061db9..7bfe112 100644 --- a/internal/pkg/cache/postmeta.go +++ b/internal/pkg/cache/postmeta.go @@ -2,9 +2,6 @@ package cache import ( "context" - "github/fthvgb1/wp-go/helper" - wp2 "github/fthvgb1/wp-go/internal/pkg/models" - "strconv" "time" ) @@ -16,43 +13,3 @@ func GetPostMetaByPostId(ctx context.Context, id uint64) (r map[string]any, err r, err = postMetaCache.GetCache(ctx, id, time.Second, ctx, id) return } - -func ToPostThumbnail(c context.Context, postId uint64) (r wp2.PostThumbnail) { - meta, err := GetPostMetaByPostId(c, postId) - if err == nil { - m, ok := meta["_thumbnail_id"] - if ok { - id, err := strconv.ParseUint(m.(string), 10, 64) - if err == nil { - mm, err := GetPostMetaByPostId(c, id) - if err == nil { - f, ok := mm["_wp_attached_file"] - if ok { - ff, ok := f.(string) - if ok && ff != "" { - r.Path = ff - } - } - tt, ok := helper.GetStrMapAnyVal[map[string]any]("_wp_attachment_metadata.sizes.post-thumbnail", mm) - if ok && tt != nil { - width, ok := tt["width"] - if ok { - w, ok := width.(int) - if ok { - r.Width = w - } - } - height, ok := tt["height"] - if ok { - h, ok := height.(int) - if ok { - r.Height = h - } - } - } - } - } - } - } - return -} diff --git a/internal/pkg/dao/postmeta.go b/internal/pkg/dao/postmeta.go index e8d6237..8c5b173 100644 --- a/internal/pkg/dao/postmeta.go +++ b/internal/pkg/dao/postmeta.go @@ -2,19 +2,20 @@ package common import ( "context" - "github.com/leeqvip/gophp" + "fmt" "github/fthvgb1/wp-go/helper" "github/fthvgb1/wp-go/internal/pkg/logs" - models2 "github/fthvgb1/wp-go/internal/pkg/models" + "github/fthvgb1/wp-go/internal/pkg/models" "github/fthvgb1/wp-go/model" "strconv" + "strings" ) func GetPostMetaByPostIds(args ...any) (r map[uint64]map[string]any, err error) { r = make(map[uint64]map[string]any) ctx := args[0].(context.Context) ids := args[1].([]uint64) - rr, err := model.Find[models2.Postmeta](ctx, model.SqlBuilder{ + rr, err := model.Find[models.Postmeta](ctx, model.SqlBuilder{ {"post_id", "in", ""}, }, "*", "", nil, nil, nil, 0, helper.SliceMap(ids, helper.ToAny[uint64])) if err != nil { @@ -25,15 +26,13 @@ func GetPostMetaByPostIds(args ...any) (r map[uint64]map[string]any, err error) r[postmeta.PostId] = make(map[string]any) } if postmeta.MetaKey == "_wp_attachment_metadata" { - meta, err := gophp.Unserialize([]byte(postmeta.MetaValue)) + metadata, err := models.AttachmentMetadata(postmeta.MetaValue) if err != nil { - logs.ErrPrintln(err, "反序列化postmeta失败", postmeta.MetaValue) + logs.ErrPrintln(err, "解析postmeta失败", postmeta.MetaId, postmeta.MetaValue) continue } - metaVal, ok := meta.(map[string]any) - if ok { - r[postmeta.PostId][postmeta.MetaKey] = metaVal - } + r[postmeta.PostId][postmeta.MetaKey] = metadata + } else { r[postmeta.PostId][postmeta.MetaKey] = postmeta.MetaValue } @@ -42,7 +41,7 @@ func GetPostMetaByPostIds(args ...any) (r map[uint64]map[string]any, err error) return } -func ToPostThumb(c context.Context, meta map[string]any, postId uint64) (r models2.PostThumbnail) { +func ToPostThumb(c context.Context, meta map[string]any, host string) (r models.PostThumbnail) { if meta != nil { m, ok := meta["_thumbnail_id"] if ok { @@ -59,20 +58,29 @@ func ToPostThumb(c context.Context, meta map[string]any, postId uint64) (r model r.Path = ff } } - tt, ok := helper.GetStrMapAnyVal[map[string]any]("_wp_attachment_metadata.sizes.post-thumbnail", mm) - if ok && tt != nil { - width, ok := tt["width"] + x, ok := mm["_wp_attachment_metadata"] + if ok { + metadata, ok := x.(models.WpAttachmentMetadata) if ok { - w, ok := width.(int) - if ok { - r.Width = w - } - } - height, ok := tt["height"] - if ok { - h, ok := height.(int) - if ok { - r.Height = h + if _, ok := metadata.Sizes["post-thumbnail"]; ok { + r.Width = metadata.Sizes["post-thumbnail"].Width + r.Height = metadata.Sizes["post-thumbnail"].Height + up := strings.Split(metadata.File, "/") + r.Srcset = strings.Join(helper.MapToSlice[string](metadata.Sizes, func(s string, size models.MetaDataFileSize) (r string, ok bool) { + up[2] = size.File + if s == "post-thumbnail" { + return + } + r = fmt.Sprintf("%s/wp-content/uploads/%s %dw", host, strings.Join(up, "/"), size.Width) + ok = true + return + }), ", ") + r.Sizes = fmt.Sprintf("(max-width: %dpx) 100vw, %dpx", r.Width, r.Width) + if r.Width >= 740 && r.Width < 767 { + r.Sizes = "(max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px" + } else if r.Width >= 767 { + r.Sizes = "(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px" + } } } } diff --git a/internal/pkg/dao/posts.go b/internal/pkg/dao/posts.go index 65c1f88..9ca7d90 100644 --- a/internal/pkg/dao/posts.go +++ b/internal/pkg/dao/posts.go @@ -6,6 +6,7 @@ import ( "fmt" "github/fthvgb1/wp-go/helper" "github/fthvgb1/wp-go/internal/pkg/models" + "github/fthvgb1/wp-go/internal/wpconfig" "github/fthvgb1/wp-go/model" "strings" "sync/atomic" @@ -42,6 +43,7 @@ func GetPostsByIds(ids ...any) (m map[uint64]models.Posts, err error) { } postsMap[post.Id] = v } + host, _ := wpconfig.Options.Load("siteurl") meta, _ := GetPostMetaByPostIds(ctx, id) for k, pp := range postsMap { if len(pp.Categories) > 0 { @@ -52,7 +54,7 @@ func GetPostsByIds(ids ...any) (m map[uint64]models.Posts, err error) { pp.CategoriesHtml = strings.Join(t, "、") mm, ok := meta[pp.Id] if ok { - thumb := ToPostThumb(ctx, mm, pp.Id) + thumb := ToPostThumb(ctx, mm, host) if thumb.Path != "" { pp.Thumbnail = thumb } diff --git a/internal/pkg/models/wp_postmeta.go b/internal/pkg/models/wp_postmeta.go index e0d6d32..e2fcfdf 100644 --- a/internal/pkg/models/wp_postmeta.go +++ b/internal/pkg/models/wp_postmeta.go @@ -1,5 +1,10 @@ package models +import ( + "github.com/leeqvip/gophp" + "github/fthvgb1/wp-go/helper" +) + type Postmeta struct { MetaId uint64 `db:"meta_id" json:"meta_id" form:"meta_id"` PostId uint64 `db:"post_id" json:"post_id" form:"post_id"` @@ -14,3 +19,62 @@ func (p Postmeta) PrimaryKey() string { func (p Postmeta) Table() string { return "wp_postmeta" } + +func (p Postmeta) AttachmentMetadata() (r WpAttachmentMetadata, err error) { + if p.MetaKey == "_wp_attachment_metadata" && p.MetaValue != "" { + unSerialize, er := gophp.Unserialize([]byte(p.MetaValue)) + if er != nil { + err = er + return + } + info, ok := unSerialize.(map[string]any) + if ok { + r, err = helper.MapToStruct[WpAttachmentMetadata](info) + } + } + return +} +func AttachmentMetadata(s string) (r WpAttachmentMetadata, err error) { + unSerialize, er := gophp.Unserialize([]byte(s)) + if er != nil { + err = er + return + } + info, ok := unSerialize.(map[string]any) + if ok { + r, err = helper.MapToStruct[WpAttachmentMetadata](info) + } + return +} + +type WpAttachmentMetadata struct { + Width int `json:"width,omitempty"` + Height int `json:"height,omitempty"` + File string `json:"file,omitempty"` + FileSize int `json:"filesize,omitempty"` + Sizes map[string]MetaDataFileSize `json:"sizes,omitempty"` + ImageMeta ImageMeta `json:"image_meta"` +} + +type ImageMeta struct { + Aperture string `json:"aperture,omitempty"` + Credit string `json:"credit,omitempty"` + Camera string `json:"camera,omitempty"` + Caption string `json:"caption,omitempty"` + CreatedTimestamp string `json:"created_timestamp,omitempty"` + Copyright string `json:"copyright,omitempty"` + FocalLength string `json:"focal_length,omitempty"` + Iso string `json:"iso,omitempty"` + ShutterSpeed string `json:"shutter_speed,omitempty"` + Title string `json:"title,omitempty"` + Orientation string `json:"orientation,omitempty"` + Keywords []string `json:"keywords,omitempty"` +} + +type MetaDataFileSize struct { + File string `json:"file,omitempty"` + Width int `json:"width,omitempty"` + Height int `json:"height,omitempty"` + MimeType string `json:"mime-type,omitempty"` + FileSize int `json:"filesize,omitempty"` +} diff --git a/internal/pkg/models/wp_posts.go b/internal/pkg/models/wp_posts.go index fff597b..f762a77 100644 --- a/internal/pkg/models/wp_posts.go +++ b/internal/pkg/models/wp_posts.go @@ -28,19 +28,22 @@ type Posts struct { CommentCount int64 `gorm:"column:comment_count" db:"comment_count" json:"comment_count" form:"comment_count"` //扩展字段 - Taxonomy string `db:"taxonomy" json:"taxonomy"` - CategoryName string `db:"category_name" json:"category_name"` - Categories []string `json:"categories"` - Tags []string `json:"tags"` - CategoriesHtml string - TagsHtml string - Thumbnail PostThumbnail + Taxonomy string `db:"taxonomy" json:"taxonomy"` + CategoryName string `db:"category_name" json:"category_name"` + Categories []string `json:"categories"` + Tags []string `json:"tags"` + CategoriesHtml string + TagsHtml string + Thumbnail PostThumbnail + AttachmentMetadata WpAttachmentMetadata } type PostThumbnail struct { Path string Width int Height int + Srcset string + Sizes string } func (w Posts) PrimaryKey() string {