package api import ( "context" "database/sql/driver" "encoding/json" "fmt" "io" "log" "net/http" "regexp" "strconv" "strings" "time" "git.satori.love/gameui/webp/models" "github.com/doug-martin/goqu/v9" "github.com/graphql-go/graphql" "github.com/thoas/go-funk" "github.com/zhenghaoz/gorse/client" ) type Image struct { ID int `json:"id" gorm:"primaryKey"` Width int `json:"width"` Height int `json:"height"` Content string `json:"content"` Remark string `json:"remark"` Description string `json:"description"` Tags string `json:"tags"` Rank string `json:"rank"` CommentNum int `json:"comment_num"` PraiseCount int `json:"praise_count" gorm:"column:praise"` CollectCount int `json:"collect_count"` ArticleID int `json:"article_id"` UserID int `json:"user_id"` CreateTime time.Time `json:"create_time"` UpdateTime time.Time `json:"update_time"` Text TextList `json:"text" gorm:"type:json"` User User `json:"user" gorm:"foreignKey:UserID"` Article Article `json:"article" gorm:"foreignKey:ArticleID"` Activity bool `json:"activity"` Emoji1 int `json:"emoji1"` Emoji2 int `json:"emoji2"` Emoji3 int `json:"emoji3"` Emoji4 int `json:"emoji4"` Emoji5 int `json:"emoji5"` Views int `json:"views"` DayRank int `json:"day_rank"` WeekRank int `json:"week_rank"` MonthRank int `json:"month_rank"` YearRank int `json:"year_rank"` AeonRank int `json:"aeon_rank"` } func (Image) TableName() string { return "web_images" } type TextList []struct { Text string `json:"text"` Confidence float64 `json:"confidence"` Coordinate [][]float64 `json:"coordinate"` } func (a *TextList) Scan(value interface{}) error { // 如果数据库中的值为NULL,则返回nil if value == nil || len(value.([]byte)) == 0 { *a = TextList{} return nil } return json.Unmarshal(value.([]byte), a) } func (a TextList) Value() (driver.Value, error) { return json.Marshal(a) } // 图像的可选字段 var imageType = graphql.NewObject(graphql.ObjectConfig{ Name: "Image", Description: "图像", Fields: graphql.Fields{ "id": &graphql.Field{Type: graphql.Int, Description: "图像ID"}, "width": &graphql.Field{Type: graphql.Int, Description: "图像宽度"}, "height": &graphql.Field{Type: graphql.Int, Description: "图像高度"}, "content": &graphql.Field{Type: graphql.String, Description: "图像内容"}, "remark": &graphql.Field{Type: graphql.String, Description: "图像备注"}, "description": &graphql.Field{Type: graphql.String, Description: "图像描述"}, "tags": &graphql.Field{Type: graphql.String, Description: "图像标签"}, "rank": &graphql.Field{Type: graphql.String, Description: "图像等级"}, "comment_num": &graphql.Field{Type: graphql.Int, Description: "评论数"}, "article_category_top_id": &graphql.Field{Type: graphql.Int, Description: "文章分类顶级ID"}, "praise_count": &graphql.Field{Type: graphql.Int, Description: "点赞数"}, "collect_count": &graphql.Field{Type: graphql.Int, Description: "收藏数"}, "create_time": &graphql.Field{Type: graphql.DateTime, Description: "图像创建时间"}, "update_time": &graphql.Field{Type: graphql.DateTime, Description: "图像更新时间"}, "activity": &graphql.Field{Type: graphql.Boolean, Description: "图像活动"}, "article": &graphql.Field{Type: articleType, Description: "图像所属文章"}, "praise": &graphql.Field{Type: graphql.Boolean, Description: "当前用户是否点赞", Resolve: func(p graphql.ResolveParams) (interface{}, error) { var user_id = p.Context.Value("user_id").(int) if user_id != 0 { var praise int64 if err := db.Table("web_praise").Where("user_id = ?", user_id).Where("praise_id = ?", p.Source.(Image).ID).Where("type = ?", 4).Count(&praise).Error; err != nil { fmt.Println(user_id, p.Source.(Image).ID, praise, "E", err) return false, nil } if praise > 0 { return true, nil } } return false, nil }}, "collect": &graphql.Field{Type: graphql.Boolean, Description: "当前用户是否收藏", Resolve: func(p graphql.ResolveParams) (interface{}, error) { var user_id = p.Context.Value("user_id").(int) if user_id != 0 { var collect int64 if err := db.Table("web_collect").Where("user_id = ?", user_id).Where("image_id = ?", p.Source.(Image).ID).Where("type = ?", 1).Count(&collect).Error; err != nil { return false, nil } if collect > 0 { return true, nil } } return false, nil }}, "collect_id": &graphql.Field{Type: graphql.Int, Description: "当前用户收藏ID", Resolve: func(p graphql.ResolveParams) (interface{}, error) { var user_id = p.Context.Value("user_id").(int) if user_id != 0 { var collect_id int if err := db.Table("web_collect").Where("user_id = ?", user_id).Where("image_id = ?", p.Source.(Image).ID).Where("type = ?", 1).Select("id").Scan(&collect_id).Error; err != nil { fmt.Println(user_id, p.Source.(Image).ID, collect_id, "E", err) return nil, nil } return collect_id, nil } return nil, nil }}, "text": &graphql.Field{ Type: graphql.NewList(graphql.NewObject(graphql.ObjectConfig{ Name: "Text", Description: "图像中的文字提取", Fields: graphql.Fields{ "text": &graphql.Field{Type: graphql.String, Description: "文字内容"}, "confidence": &graphql.Field{Type: graphql.Float, Description: "置信度"}, "coordinate": &graphql.Field{Type: &graphql.List{OfType: graphql.NewList(graphql.Float)}, Description: "文字坐标"}, }, })), Description: "图像中的文字", Args: graphql.FieldConfigArgument{ "text": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选含有指定文字的列"}, }, Resolve: func(p graphql.ResolveParams) (interface{}, error) { if p.Args["text"] != nil { var texts TextList for _, text := range p.Source.(Image).Text { if strings.Contains(text.Text, p.Args["text"].(string)) { texts = append(texts, text) } } return texts, nil } return p.Source.(Image).Text, nil }, }, "user": &graphql.Field{Type: userType, Description: "图像所属用户"}, "emoji1": &graphql.Field{Type: graphql.Int, Description: "表情1数量"}, "emoji2": &graphql.Field{Type: graphql.Int, Description: "表情2数量"}, "emoji3": &graphql.Field{Type: graphql.Int, Description: "表情3数量"}, "emoji4": &graphql.Field{Type: graphql.Int, Description: "表情4数量"}, "emoji5": &graphql.Field{Type: graphql.Int, Description: "表情5数量"}, "views": &graphql.Field{Type: graphql.Int, Description: "浏览量"}, "day_rank": &graphql.Field{Type: graphql.Int, Description: "日榜"}, "week_rank": &graphql.Field{Type: graphql.Int, Description: "周榜"}, "month_rank": &graphql.Field{Type: graphql.Int, Description: "月榜"}, "year_rank": &graphql.Field{Type: graphql.Int, Description: "年榜"}, "aeon_rank": &graphql.Field{Type: graphql.Int, Description: "总榜"}, }, }) //image.AddFieldConfig("user", &graphql.Field{Type: userType, Description: "图像所属用户"}) //image.AddFieldConfig("similars", &graphql.Field{Type: graphql.NewList(image), Description: "相似的图像", Resolve: func(p graphql.ResolveParams) (interface{}, error) { // return []Image{}, nil //}}) var ImageItem = &graphql.Field{ Name: "image", Description: "单张图片", Type: imageType, Args: graphql.FieldConfigArgument{ "id": &graphql.ArgumentConfig{Type: graphql.Int, Description: "根据ID获取图片"}, }, Resolve: func(p graphql.ResolveParams) (interface{}, error) { img := Image{ID: p.Args["id"].(int)} query := db.Limit(1) for index, item := range LoadItem(p.Info.FieldASTs[0].SelectionSet.Selections) { fmt.Println(index, item) query = query.Preload(item) } if err := query.First(&img).Error; err != nil { log.Println("获取图片失败", err) return nil, err } return img, nil }, } var ImageItems = &graphql.Field{ Name: "images", Description: "图像列表", Type: graphql.NewObject(graphql.ObjectConfig{ Name: "ImageConnection", Description: "条件筛选图像列表", Fields: graphql.Fields{ "list": &graphql.Field{Type: graphql.NewList(imageType), Description: "图像列表"}, "total": &graphql.Field{Type: graphql.Int, Description: "图像总数"}, }, }), Args: graphql.FieldConfigArgument{ "activity": &graphql.ArgumentConfig{Type: graphql.Boolean, Description: "按是否活动筛选图像"}, "style": &graphql.ArgumentConfig{Type: graphql.String, Description: "按游戏风格筛选图像"}, "device": &graphql.ArgumentConfig{Type: graphql.String, Description: "按游戏平台筛选图像"}, "orientation": &graphql.ArgumentConfig{Type: graphql.String, Description: "按游戏版式筛选图像"}, "era": &graphql.ArgumentConfig{Type: graphql.String, Description: "按游戏年份筛选图像"}, "category_id": &graphql.ArgumentConfig{Type: graphql.String, Description: "按游戏类型筛选图像(支持多选)"}, "tags": &graphql.ArgumentConfig{Type: graphql.String, Description: "按游戏标签筛选图像(支持多选)"}, "images_desc": &graphql.ArgumentConfig{Type: graphql.String, Description: "按游戏归类筛选图像(支持多选)"}, "article_tags": &graphql.ArgumentConfig{Type: graphql.String, Description: "按文章标签筛选图像(支持多选)"}, "color": &graphql.ArgumentConfig{Type: graphql.String, Description: "按主色调筛选图像, 使用十六进制颜色代码(#FF1414)"}, "explorer_id": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选图像中指定收藏夹的"}, "collect_id": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选图像中指定收藏夹的"}, "collect": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选图像中指定用户收藏过的"}, "praise": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选图像中指定用户点赞过的"}, "follower": &graphql.ArgumentConfig{Type: graphql.Int, Description: "获取指定ID用户的关注列表发布的图像"}, "interest": &graphql.ArgumentConfig{Type: graphql.Int, Description: "获取指定ID用户的兴趣推荐图像"}, "similar": &graphql.ArgumentConfig{Type: graphql.Int, Description: "获取与指定ID图像相似的图像"}, "id": &graphql.ArgumentConfig{Type: graphql.Int, Description: "获取指定ID的图像"}, "width": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选图像中指定宽度的"}, "height": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选图像中指定高度的"}, "comment_num": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选图像中评论数等于指定值的"}, "praise_count": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选图像中点赞数等于指定值的"}, "collect_count": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选图像中收藏数等于指定值的"}, "article_id": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选图像中属于指定文章ID的"}, "user_id": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选图像中属于指定用户ID的"}, "sort": &graphql.ArgumentConfig{Type: graphql.String, Description: "排序方法", DefaultValue: "id"}, "content": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选图像中含有指定内容的"}, "remark": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选图像中含有指定备注的"}, "description": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选图像中含有指定描述的"}, "rank": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选图像中含有指定排名的"}, "text": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选图像中含有指定文字的"}, "create_time": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选图像中创建时间等于指定值的"}, "update_time": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选图像中更新时间等于指定值的"}, "first": &graphql.ArgumentConfig{Type: graphql.Int, Description: "翻页参数(傳回清單中的前n個元素)"}, "last": &graphql.ArgumentConfig{Type: graphql.Int, Description: "翻页参数(傳回清單中的最後n個元素)"}, "after": &graphql.ArgumentConfig{Type: graphql.Int, Description: "翻页参数(傳回清單中指定遊標之後的元素)"}, "before": &graphql.ArgumentConfig{Type: graphql.Int, Description: "翻页参数(傳回清單中指定遊標之前的元素)"}, "order": &graphql.ArgumentConfig{Type: orderType, Description: "排序方向", DefaultValue: "ASC"}, }, Resolve: func(p graphql.ResolveParams) (interface{}, error) { var total int var images []Image var query = goqu.Dialect("mysql").From("web_images") // 参数映射 var argFormat = []string{"id", "width", "height", "content", "remark", "description", "rank", "comment_num", "praise_count", "collect_count", "article_id", "user_id"} // 筛选条件 for _, format := range argFormat { if p.Args[format] != nil { fmt.Println(format, p.Args[format]) query = query.Where(goqu.Ex{fmt.Sprintf("web_images.%s", format): p.Args[format]}) } } // 筛选:提取文字 if p.Args["text"] != nil { resp, err := models.ZincSearch(map[string]interface{}{ "query": map[string]interface{}{ "bool": map[string]interface{}{ "must": []map[string]interface{}{ { "match_phrase": map[string]string{"text": p.Args["text"].(string)}, }, }, }, }, "_source": false, "sort": []string{"_score"}, "size": 200000, }) if err != nil { fmt.Println("ZincSearch 获取图像列表失败", err) return nil, err } var item []int for _, hit := range resp.Hits.Hits { num, _ := strconv.Atoi(hit.ID) item = append(item, num) } if len(item) == 0 { return map[string]interface{}{"list": []Image{}, "total": 0}, nil } query = query.Where(goqu.Ex{"web_images.id": goqu.Op{"in": item}}) total = len(item) } // 筛选:相似图像 if p.Args["similar"] != nil { var item []int for _, id := range models.GetSimilarImagesIdList(p.Args["similar"].(int), 200) { item = append(item, int(id)) } if len(item) == 0 { return map[string]interface{}{"list": []Image{}, "total": 0}, nil } query = query.Where(goqu.Ex{"web_images.id": goqu.Op{"in": item}}).Select("web_images.id", goqu.L( fmt.Sprintf("ROW_NUMBER() OVER(ORDER BY FIELD(%s, %s))", "web_images.id", regexp.MustCompile(`[\[\]]`).ReplaceAllString(strings.Join(strings.Fields(fmt.Sprint(item)), ", "), "")), ).As("row_num")) total = len(item) // 收集阅读行为 if p.Context.Value("user_id") != nil && p.Args["after"] == nil { ctx := context.Background() user_id := p.Context.Value("user_id").(int) feedbacks := []client.Feedback{{ FeedbackType: "read", UserId: strconv.Itoa(user_id), ItemId: strconv.Itoa(p.Args["similar"].(int)), Timestamp: time.Now().Format("2006-01-02 15:04:05"), }} if _, err := gorse.InsertFeedback(ctx, feedbacks); err != nil { fmt.Println("写入阅读记录失败", err) } } } // 筛选:兴趣推荐 if p.Args["interest"] != nil { fmt.Println("interest:", p.Args["interest"]) user_id := p.Args["interest"].(int) fmt.Println("interest1:", user_id) list, err := GetRecommend(user_id, []string{}) fmt.Println("interest2:", list, err) if err != nil { fmt.Println("GetRecommend 获取兴趣推荐失败", err) return map[string]interface{}{"list": []Image{}, "total": 0}, nil } if len(list) == 0 { return map[string]interface{}{"list": []Image{}, "total": 0}, nil } fmt.Println("Interest:", user_id, list) query = query.Where(goqu.Ex{"web_images.id": goqu.Op{"in": list}}).Select("web_images.id", goqu.L( fmt.Sprintf("ROW_NUMBER() OVER(ORDER BY FIELD(%s, %s))", "web_images.id", regexp.MustCompile(`[\[\]]`).ReplaceAllString(strings.Join(strings.Fields(fmt.Sprint(list)), ", "), "")), ).As("row_num")) total = len(list) } // 筛选:时间段 applyTimeCondition := func(name string, str string) { parts := strings.Split(str, "-") query = query.Where(goqu.Ex{fmt.Sprintf("YEAR(%s)", name): parts[0]}) if len(parts) > 1 { query = query.Where(goqu.Ex{fmt.Sprintf("MONTH(%s)", name): parts[1]}) } if len(parts) > 2 { query = query.Where(goqu.Ex{fmt.Sprintf("DAY(%s)", name): parts[2]}) } } // 数据库中筛选:创建时间段 if p.Args["create_time"] != nil { applyTimeCondition("create_time", p.Args["create_time"].(string)) } // 数据库中筛选:更新时间段 if p.Args["update_time"] != nil { applyTimeCondition("update_time", p.Args["update_time"].(string)) } // 数据库中筛选:按是否活动 if activity, ok := p.Args["activity"].(bool); ok { query = query.Where(goqu.Ex{"web_images.activity": map[bool]string{true: "1", false: "0"}[activity]}) } // 数据库中筛选:按游戏标签 if p.Args["tags"] != nil { tags := strings.Split(strings.ReplaceAll(p.Args["tags"].(string), " ", ""), ",") var conditions []string for _, tag := range tags { conditions = append(conditions, fmt.Sprintf("FIND_IN_SET('%s', web_article.tags)", tag)) } query = query.Join(goqu.T("web_article"), goqu.On( goqu.I("web_images.article_id").Eq(goqu.I("web_article.id")), goqu.L(strings.Join(conditions, " AND ")), )) } // 数据库中筛选:按游戏分类 if p.Args["images_desc"] != nil { tags := strings.Split(strings.ReplaceAll(p.Args["images_desc"].(string), " ", ""), ",") for _, tag := range tags { query = query.Where(goqu.L("MATCH(web_images.images_desc) AGAINST (? IN NATURAL LANGUAGE MODE)", tag)) } } // 数据库中筛选:喜欢的截图 if p.Args["praise"] != nil { query = query.Join(goqu.T("web_praise"), goqu.On( goqu.I("web_images.id").Eq(goqu.I("web_praise.praise_id")), goqu.I("web_praise.user_id").Eq(p.Args["praise"]), goqu.I("web_praise.type").Eq(4), )) } var collect_params []goqu.Expression = []goqu.Expression{ goqu.I("web_images.id").Eq(goqu.I("web_collect.image_id")), } // 数据库中筛选:用户收藏的截图 if p.Args["collect"] != nil { collect_params = append(collect_params, goqu.I("web_collect.user_id").Eq(p.Args["collect"])) } // 数据库中筛选:收藏夹中的截图 if p.Args["collect_id"] != nil { collect_params = append(collect_params, goqu.I("web_collect.collect_id").Eq(p.Args["collect_id"])) } // 数据库中筛选:收藏夹中的截图 if p.Args["explorer_id"] != nil { collect_params = append(collect_params, goqu.I("web_collect.explorer_id").Eq(p.Args["explorer_id"])) } if len(collect_params) > 1 { query = query.Join(goqu.T("web_collect"), goqu.On(collect_params...)) } var conditions []goqu.Expression = []goqu.Expression{ goqu.I("web_images.article_id").Eq(goqu.I("web_article.id")), } // 按游戏风格筛选图像 if p.Args["style"] != nil { conditions = append(conditions, goqu.I("web_article.style").Eq(p.Args["style"])) } // 按游戏平台筛选图像 if p.Args["device"] != nil { conditions = append(conditions, goqu.I("web_article.device").Eq(p.Args["device"])) } // 按游戏版式筛选图像 if p.Args["orientation"] != nil { conditions = append(conditions, goqu.I("web_article.orientation").Eq(p.Args["orientation"])) } // 按游戏年份筛选图像 if p.Args["era"] != nil { conditions = append(conditions, goqu.I("web_article.era").Eq(p.Args["era"])) } // 按游戏类型筛选图像(逗号分割且去除空格) if p.Args["category_id"] != nil { category_ids := strings.Split(strings.ReplaceAll(p.Args["category_id"].(string), " ", ""), ",") conditions = append(conditions, goqu.I("web_article.category_id").In(category_ids)) } // 按游戏标签筛选图像 if p.Args["article_tags"] != nil { tags := strings.Split(strings.ReplaceAll(p.Args["article_tags"].(string), " ", ""), ", ") for _, tag := range tags { //conditions = append(conditions, goqu.L("MATCH(web_article.tags) AGAINST (? IN NATURAL LANGUAGE MODE)", tag)) conditions = append(conditions, goqu.L("FIND_IN_SET(?, web_article.tags)", tag)) } } if len(conditions) > 1 { query = query.Join(goqu.T("web_article"), goqu.On(conditions...)) } // 数据库中筛选:按关注列表 if p.Args["follower"] != nil { query = query.Join(goqu.T("web_fans"), goqu.On( goqu.I("web_images.user_id").Eq(goqu.I("web_fans.blogger_id")), goqu.I("web_fans.follower_id").Eq(p.Args["follower"]), )) } // 数据库中筛选: 按图像主色调颜色筛选 if p.Args["color"] != nil { // hexToRGB 将十六进制颜色转换为 RGB hexToRGB := func(hex string) (int, int, int, error) { // 去掉 # 号 if strings.HasPrefix(hex, "#") { hex = hex[1:] } // 检查是否是有效的十六进制颜色 if len(hex) != 6 { return 0, 0, 0, fmt.Errorf("invalid hex color") } // 解析红色、绿色、蓝色通道的十六进制值 r, err := strconv.ParseInt(hex[:2], 16, 0) if err != nil { return 0, 0, 0, err } g, err := strconv.ParseInt(hex[2:4], 16, 0) if err != nil { return 0, 0, 0, err } b, err := strconv.ParseInt(hex[4:], 16, 0) if err != nil { return 0, 0, 0, err } return int(r), int(g), int(b), nil } // 逗号分割且去除空格 colors := strings.Split(strings.ReplaceAll(p.Args["color"].(string), " ", ""), ",") for index, color := range colors { var precision int = 10 if strings.Contains(color, ":") { re := regexp.MustCompile(`^#([0-9a-fA-F]{6}):(\d+)$`) matches := re.FindStringSubmatch(color) num, err := strconv.Atoi(matches[2]) if err != nil { fmt.Println("数字精度转换失败:", err) return nil, err } color = "#" + matches[1] precision = num } r, g, b, err := hexToRGB(color) if err != nil { fmt.Println("hexToRGB", index, err) return nil, err } fmt.Println(color, r, g, b, precision) query1, _, _ := goqu.Dialect("mysql").From("web_images").Select("id").Where( goqu.And( goqu.L(fmt.Sprintf("color_%d_r", 0)).Gt(r-precision), goqu.L(fmt.Sprintf("color_%d_r", 0)).Lt(r+precision), goqu.L(fmt.Sprintf("color_%d_g", 0)).Gt(g-precision), goqu.L(fmt.Sprintf("color_%d_g", 0)).Lt(g+precision), goqu.L(fmt.Sprintf("color_%d_b", 0)).Gt(b-precision), goqu.L(fmt.Sprintf("color_%d_b", 0)).Lt(b+precision), ), ).ToSQL() query2, _, _ := goqu.Dialect("mysql").From("web_images").Select("id").Where( goqu.And( goqu.L(fmt.Sprintf("color_%d_r", 1)).Gt(r-precision), goqu.L(fmt.Sprintf("color_%d_r", 1)).Lt(r+precision), goqu.L(fmt.Sprintf("color_%d_g", 1)).Gt(g-precision), goqu.L(fmt.Sprintf("color_%d_g", 1)).Lt(g+precision), goqu.L(fmt.Sprintf("color_%d_b", 1)).Gt(b-precision), goqu.L(fmt.Sprintf("color_%d_b", 1)).Lt(b+precision), ), ).ToSQL() query = query.Join( goqu.L(fmt.Sprintf("(%s UNION %s) AS w", query1, query2)), goqu.On(goqu.Ex{"web_images.id": goqu.I("w.id")}), ) } } // 如果没有外部排序则使用指定排序(正则sort只能是字母数字下划线) if p.Args["similar"] == nil && p.Args["interest"] == nil && p.Args["praise"] == nil { // 如果查询了 total 字段 if existField(p.Info.FieldASTs[0].SelectionSet.Selections, "total") { sql, _, _ := query.ToSQL() sql = strings.Replace(sql, "SELECT *", "SELECT COUNT(*)", 1) if err := db.Raw(sql).Scan(&total).Error; err != nil { return nil, err } } query = query.Select("web_images.id", goqu.L(fmt.Sprintf( "ROW_NUMBER() OVER(ORDER BY web_images.%s %s)", regexp.MustCompile(`[^a-zA-Z0-9_]`).ReplaceAllString(p.Args["sort"].(string), ""), p.Args["order"].(string), )).As("row_num")) } // 图像按点赞顺序排序 p.Args["sort"].(string) == "praise_time" if p.Args["praise"] != nil { query = query.Select("web_images.id", goqu.L( fmt.Sprintf("ROW_NUMBER() OVER(ORDER BY web_praise.%s %s)", "id", p.Args["order"].(string)), ).As("row_num")) } // 取所有数据的前N条(筛取属于游戏的且非封面的图像) sql, _, _ := query.Where(goqu.Ex{"article_category_top_id": 22}).Where(goqu.Ex{"web_images.type": 0}).ToSQL() // 遊標截取篩選結果集的前N条 var cursor string if p.Args["after"] != nil { cursor = fmt.Sprintf(`WHERE row_num > (SELECT row_num FROM RankedArticles WHERE RankedArticles.id = %d)`, p.Args["after"].(int)) } var limit int = 10 if p.Args["first"] != nil { limit = p.Args["first"].(int) } else if p.Args["last"] != nil { limit = p.Args["last"].(int) } sql = fmt.Sprintf(` WITH RankedArticles AS (%s) SELECT web_images.* FROM web_images INNER JOIN( SELECT id, row_num FROM RankedArticles %s ) AS LimitedRanked ON LimitedRanked.id = web_images.id ORDER BY LimitedRanked.row_num ASC LIMIT %d `, sql, cursor, limit) fmt.Println(sql) if err := db.Raw(sql).Scan(&images).Error; err != nil { fmt.Println("获取图像列表失败", err) return nil, err } var ids []int for _, item := range images { ids = append(ids, item.ID) } var find = db.Where("web_images.id IN ?", ids).Select("*", "CASE WHEN activity = '1' THEN TRUE ELSE FALSE END") for _, item := range LoadItem(p.Info.FieldASTs[0].SelectionSet.Selections) { find = find.Preload(item) } if len(ids) == 0 { return map[string]interface{}{"list": []Image{}, "total": 0}, nil } orderClause := fmt.Sprintf("FIELD(id, %s)", regexp.MustCompile(`[\[\]]`).ReplaceAllString(strings.Join(strings.Fields(fmt.Sprint(ids)), ","), "")) if err := find.Order(orderClause).Find(&images).Error; err != nil { fmt.Println("获取图像列表失败", err) return nil, err } var items = ListItem(p.Info.FieldASTs[0].SelectionSet.Selections) if funk.Contains(items, "views") { type ApiResponse struct { ID int `json:"id"` Count int `json:"count"` } // 0. 收集要查询的 ID var ids []int for x := range images { ids = append(ids, images[x].ID) } idx := strings.Trim(strings.Replace(fmt.Sprint(ids), " ", ",", -1), "[]") // 1. 发送 GET 请求 resp, err := http.Get("http://localhost:6005/api/get_views/截图?ids=" + idx) if err != nil { fmt.Println("Error making GET request:", err) return nil, err } defer resp.Body.Close() // 2. 检查 HTTP 状态码 if resp.StatusCode != http.StatusOK { fmt.Printf("Request failed with status code: %d\n", resp.StatusCode) return nil, err } // 3. 读取响应体 body, err := io.ReadAll(resp.Body) if err != nil { fmt.Println("Error reading response body:", err) return nil, err } // 4. 解析 JSON 数据到结构体 var data []ApiResponse err = json.Unmarshal(body, &data) if err != nil { fmt.Println("Error unmarshalling JSON:", err) return nil, err } // 5. 赋值到数据集 for _, item := range data { for i := range images { if images[i].ID == item.ID { images[i].Views = item.Count continue } } } } return map[string]interface{}{ "list": images, "total": total, }, nil }, }