package api import ( "fmt" "log" "reflect" "regexp" "strconv" "strings" "git.satori.love/gameui/webp/models" "github.com/doug-martin/goqu/v9" _ "github.com/doug-martin/goqu/v9/dialect/mysql" "github.com/graphql-go/graphql" "github.com/graphql-go/graphql/language/ast" "github.com/jmoiron/sqlx" "github.com/mitchellh/mapstructure" "github.com/thoas/go-funk" "gorm.io/driver/mysql" "gorm.io/gorm" ) var db *gorm.DB var err error func LoadItem(requestedFields []ast.Selection) (data []string) { var items = []string{"user", "article"} for _, field := range requestedFields { fieldAST, _ := field.(*ast.Field) if funk.Contains(items, fieldAST.Name.Value) { name := fieldAST.Name.Value name = strings.ToUpper(string(name[0])) + name[1:] data = append(data, name) for _, str := range LoadItem(fieldAST.SelectionSet.Selections) { data = append(data, name+"."+str) } } if fieldAST.Name.Value == "list" { for _, str := range LoadItem(fieldAST.SelectionSet.Selections) { data = append(data, str) } } } return data } // 自动生成 GraphQL 类型的函数 func generateGraphQLType(model interface{}) (*graphql.Object, error) { modelType := reflect.TypeOf(model) if modelType.Kind() != reflect.Struct { return nil, fmt.Errorf("model must be a struct") } fields := graphql.Fields{} for i := 0; i < modelType.NumField(); i++ { field := modelType.Field(i) fieldType := graphql.String // 默认使用字符串类型 // 这里可以根据需要添加更多类型映射 switch field.Type.Kind() { case reflect.String: fieldType = graphql.String case reflect.Int: fieldType = graphql.Int case reflect.Bool: fieldType = graphql.Boolean } fields[field.Name] = &graphql.Field{ Type: fieldType, } } return graphql.NewObject(graphql.ObjectConfig{ Name: modelType.Name(), Fields: fields, }), nil } func NewSchema(config Config) (graphql.Schema, error) { db, err = gorm.Open(mysql.Open(fmt.Sprintf( "%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", config.Mysql.UserName, config.Mysql.Password, config.Mysql.Host, config.Mysql.Port, config.Mysql.Database, )), &gorm.Config{}) if err != nil { log.Fatal("failed to connect to database:", err) } // 打开数据库连接 connection, err := sqlx.Connect("mysql", fmt.Sprintf( "%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", config.Mysql.UserName, config.Mysql.Password, config.Mysql.Host, config.Mysql.Port, config.Mysql.Database, )) if err != nil { log.Fatalln("连接数据库失败", err) } // 用户的可选字段 user := graphql.NewObject(graphql.ObjectConfig{ Name: "User", Description: "用户信息", Fields: graphql.Fields{ "id": &graphql.Field{Type: graphql.Int, Description: "用户ID"}, "user_name": &graphql.Field{Type: graphql.String, Description: "用户名"}, "avatar": &graphql.Field{Type: graphql.String, Description: "用户头像"}, "rank": &graphql.Field{Type: graphql.String, Description: "用户等级"}, "price": &graphql.Field{Type: graphql.Int, Description: "用户金币"}, "create_time": &graphql.Field{Type: graphql.DateTime, Description: "用户创建时间"}, "update_time": &graphql.Field{Type: graphql.DateTime, Description: "用户更新时间"}, }, }) // 文章的可选字段 article := graphql.NewObject(graphql.ObjectConfig{ Name: "Article", Description: "文章", Fields: graphql.Fields{ "id": &graphql.Field{Type: graphql.Int, Description: "ID"}, "title": &graphql.Field{Type: graphql.String, Description: "标题"}, "orientation": &graphql.Field{Type: graphql.String, Description: "方向"}, "device": &graphql.Field{Type: graphql.String, Description: "设备"}, "era": &graphql.Field{Type: graphql.String, Description: "游戏上线年份"}, "tags": &graphql.Field{Type: graphql.String, Description: "标签"}, "user": &graphql.Field{Type: user, Description: "所属用户"}, "create_time": &graphql.Field{Type: graphql.DateTime, Description: "创建时间"}, "update_time": &graphql.Field{Type: graphql.DateTime, Description: "更新时间"}, }, }) // 分类的可选字段 category := graphql.NewObject(graphql.ObjectConfig{ Name: "Category", Description: "分类", Fields: graphql.Fields{ "id": &graphql.Field{Type: graphql.Int, Description: "分类ID"}, "title": &graphql.Field{Type: graphql.String, Description: "分类标题"}, "keyword": &graphql.Field{Type: graphql.String, Description: "分类关键词"}, "parent_id": &graphql.Field{Type: graphql.Int, Description: "分类父级ID"}, "create_time": &graphql.Field{Type: graphql.DateTime, Description: "分类创建时间"}, "update_time": &graphql.Field{Type: graphql.DateTime, Description: "分类更新时间"}, "status": &graphql.Field{Type: graphql.Int, Description: "分类状态"}, "content": &graphql.Field{Type: graphql.String, Description: "分类内容"}, "sort": &graphql.Field{Type: graphql.Int, Description: "分类排序"}, "image": &graphql.Field{Type: graphql.String, Description: "分类图片"}, "image_num": &graphql.Field{Type: graphql.Int, Description: "分类图片数量"}, "article_num": &graphql.Field{Type: graphql.Int, Description: "分类文章数量"}, }, }) // 图像的可选字段 image := 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: article, 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("image_id = ?", p.Source.(Image).ID).Count(&praise); err != nil { 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).Count(&collect); 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).First(&collect_id); err != nil { 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 }, }, }, }) image.AddFieldConfig("user", &graphql.Field{Type: user, Description: "图像所属用户"}) image.AddFieldConfig("similars", &graphql.Field{Type: graphql.NewList(image), Description: "相似的图像", Resolve: func(p graphql.ResolveParams) (interface{}, error) { return []Image{}, nil }}) // 将 list 中的字段提取出来用于查询 get_fields := func(requestedFields []ast.Selection) (fields []string) { for _, field := range requestedFields { fieldAST, _ := field.(*ast.Field) if fieldAST.Name.Value == "list" { for _, field := range fieldAST.SelectionSet.Selections { fieldAST, _ := field.(*ast.Field) fields = append(fields, fieldAST.Name.Value) } } } return fields } orderType := graphql.NewEnum(graphql.EnumConfig{ Name: "OrderType", Description: "排序类型", Values: graphql.EnumValueConfigMap{ "ASC": &graphql.EnumValueConfig{ Value: "ASC", Description: "升序", }, "DESC": &graphql.EnumValueConfig{ Value: "DESC", Description: "降序", }, }, }) schema, err := graphql.NewSchema(graphql.SchemaConfig{Query: graphql.NewObject(graphql.ObjectConfig{Name: "RootQuery", Fields: graphql.Fields{ "categorys": &graphql.Field{ Name: "categorys", Description: "分类列表", Type: graphql.NewObject(graphql.ObjectConfig{ Name: "CategoryConnection", Description: "条件筛选分类列表", Fields: graphql.Fields{ "list": &graphql.Field{Type: graphql.NewList(category), Description: "分类列表"}, "total": &graphql.Field{Type: graphql.Int, Description: "分类总数"}, }, }), Args: graphql.FieldConfigArgument{ "id": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选分类中指定ID的"}, "title": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选分类中含有指定标题的"}, "keyword": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选分类中含有指定关键词的"}, "parent_id": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选分类中含有指定父级ID的"}, "create_time": &graphql.ArgumentConfig{Type: graphql.DateTime, Description: "筛选分类中创建时间等于指定值的"}, "update_time": &graphql.ArgumentConfig{Type: graphql.DateTime, Description: "筛选分类中更新时间等于指定值的"}, "first": &graphql.ArgumentConfig{Type: graphql.Int, Description: "翻页参数(傳回清單中的前n個元素)"}, "last": &graphql.ArgumentConfig{Type: graphql.Int, Description: "翻页参数(傳回清單中的最後n個元素)"}, "after": &graphql.ArgumentConfig{Type: graphql.String, Description: "翻页参数(傳回清單中指定遊標之後的元素)"}, "before": &graphql.ArgumentConfig{Type: graphql.String, Description: "翻页参数(傳回清單中指定遊標之前的元素)"}, }, Resolve: func(p graphql.ResolveParams) (interface{}, error) { var categorys []Category var total int var err error // 获取筛选条件 var arg struct { ID int Title string Keyword string ParentID int First int Last int After string Before string } mapstructure.Decode(p.Args, &arg) var limit int = 10 if arg.First != 0 { limit = arg.First } else if arg.Last != 0 { limit = arg.Last } if err := db.Limit(limit).Where("id > 0").Find(&categorys).Error; err != nil { return nil, err } return map[string]interface{}{ "list": categorys, "total": total, }, err }, }, "users": &graphql.Field{ Name: "users", Description: "用户列表", Type: graphql.NewObject(graphql.ObjectConfig{ Name: "UserConnection", Description: "条件筛选用户列表", Fields: graphql.Fields{ "list": &graphql.Field{Type: graphql.NewList(user), Description: "用户列表"}, "total": &graphql.Field{Type: graphql.Int, Description: "用户总数"}, }, }), Args: graphql.FieldConfigArgument{ "id": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选用户中指定ID的"}, "user_name": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选用户中含有指定用户名的"}, "avatar": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选用户中含有指定头像的"}, "rank": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选用户中含有指定等级的"}, "create_time": &graphql.ArgumentConfig{Type: graphql.DateTime, Description: "筛选用户中创建时间等于指定值的"}, "update_time": &graphql.ArgumentConfig{Type: graphql.DateTime, Description: "筛选用户中更新时间等于指定值的"}, "text": &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.String, Description: "翻页参数(傳回清單中指定遊標之後的元素)"}, "before": &graphql.ArgumentConfig{Type: graphql.String, Description: "翻页参数(傳回清單中指定遊標之前的元素)"}, }, Resolve: func(p graphql.ResolveParams) (interface{}, error) { var where []string if p.Args["id"] != nil { where = append(where, fmt.Sprintf("id=%d", p.Args["id"])) } if p.Args["user_name"] != nil { where = append(where, fmt.Sprintf("user_name='%s'", p.Args["user_name"])) } // 筛选条件 where_str := strings.Join(where, " AND ") if where_str != "" { where_str = "WHERE " + where_str } var query strings.Builder var users []User var total int fields := strings.Join(get_fields(p.Info.FieldASTs[0].SelectionSet.Selections), ",") query.WriteString(fmt.Sprintf("SELECT %s FROM web_member %s LIMIT %d OFFSET %d", fields, where_str, 10, 0)) if err := connection.Select(&users, query.String()); err != nil { fmt.Println("获取用户列表失败", err) return nil, err } if len(users) > 0 { query.Reset() query.WriteString(fmt.Sprintf("SELECT COUNT(*) FROM web_member %s", where_str)) if err := connection.Get(&total, query.String()); err != nil { fmt.Println("获取用户总数失败", err) return nil, err } } return map[string]interface{}{ "list": users, "total": total, }, nil }, }, "images": &graphql.Field{ Name: "images", Description: "图像列表", Type: graphql.NewObject(graphql.ObjectConfig{ Name: "ImageConnection", Description: "条件筛选图像列表", Fields: graphql.Fields{ "list": &graphql.Field{Type: graphql.NewList(image), Description: "图像列表"}, "total": &graphql.Field{Type: graphql.Int, Description: "图像总数"}, }, }), Args: graphql.FieldConfigArgument{ "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的"}, "era": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选图像中指定上线年份的"}, "device": &graphql.ArgumentConfig{Type: graphql.String, Description: "游戏设备"}, "sort": &graphql.ArgumentConfig{Type: graphql.String, Description: "排序方法", DefaultValue: "id"}, "orientation": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选横屏竖屏图像"}, "content": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选图像中含有指定内容的"}, "remark": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选图像中含有指定备注的"}, "description": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选图像中含有指定描述的"}, "tags": &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 args struct { First int Last int After int Before int Text string Interest int Similar int Sort string Order string } mapstructure.Decode(p.Args, &args) var total int var images []Image var query = goqu.Dialect("mysql").From("web_images") // 参数映射 var argFormat = []string{"id", "width", "height", "content", "remark", "description", "tags", "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 args.Text != "" { resp, err := models.ZincSearch(map[string]interface{}{ "query": map[string]interface{}{ "bool": map[string]interface{}{ "must": []map[string]interface{}{ { //"query_string": map[string]string{"query": "text:" + args.Text}, "match_phrase": map[string]string{"text": args.Text}, }, }, }, }, "sort": []string{ "_score", }, //"from": 0, "size": 1000, }) 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}}).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")) } // 筛选:相似图像 if args.Similar != 0 { var item []int for _, id := range models.GetSimilarImagesIdList(args.Similar, 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")) } // 如果没有外部排序则使用指定排序 if p.Args["text"] == nil && p.Args["similar"] == nil { query = query.Select("web_images.id", goqu.L( fmt.Sprintf("ROW_NUMBER() OVER(ORDER BY web_images.%s %s)", p.Args["sort"], p.Args["order"]), ).As("row_num")) } // 筛选:兴趣推荐 if args.Interest != 0 { fmt.Println("Interest:", args.Interest) } // 筛选:时间段 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 p.Args["orientation"] != nil { query = query.Where(goqu.Ex{"article_orientation": p.Args["orientation"]}) } // 数据库中筛选:喜欢的截图 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"]), )) } // 数据库中筛选:用户收藏的截图 if p.Args["collect"] != nil { query = query.Join(goqu.T("web_collect"), goqu.On( goqu.I("web_images.id").Eq(goqu.I("web_collect.image_id")), goqu.I("web_collect.user_id").Eq(p.Args["collect"]), )) } // 数据库中筛选:收藏夹中的截图 if p.Args["collect_id"] != nil { query = query.Join(goqu.T("web_collect"), goqu.On( goqu.I("web_images.id").Eq(goqu.I("web_collect.image_id")), goqu.I("web_collect.collect_id").Eq(p.Args["collect_id"]), )) } // 数据库中筛选:收藏夹中的截图 if p.Args["explorer_id"] != nil { query = query.Join(goqu.T("web_collect"), goqu.On( goqu.I("web_images.id").Eq(goqu.I("web_collect.image_id")), goqu.I("web_collect.explorer_id").Eq(p.Args["explorer_id"]), )) } // 数据库中筛选:游戏设备 if p.Args["device"] != nil { query = query.Join(goqu.T("web_article"), goqu.On( goqu.I("web_images.article_id").Eq(goqu.I("web_article.id")), goqu.I("web_article.device").Eq(p.Args["device"]), )) } // 数据库中筛选:游戏上线年份 if p.Args["era"] != nil { query = query.Join(goqu.T("web_article"), goqu.On( goqu.I("web_images.article_id").Eq(goqu.I("web_article.id")), goqu.I("web_article.era").Eq(p.Args["era"]), )) } // 数据库中筛选:按关注列表 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"]), )) } // 取所有数据的前N条 sql, _, _ := query.Where(goqu.Ex{"article_category_top_id": 22}).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 args.First != 0 { limit = args.First } else if args.Last != 0 { limit = args.Last } sql = fmt.Sprintf(` WITH RankedArticles AS (%s) SELECT * 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 LIMIT %d `, sql, cursor, limit) fmt.Println("cursor:", cursor, limit) 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) for index, item := range LoadItem(p.Info.FieldASTs[0].SelectionSet.Selections) { fmt.Println(index, item) 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 } return map[string]interface{}{ "list": images, "total": total, }, nil }, }, "articles": &graphql.Field{ Name: "Articles", Description: "文章列表", Type: graphql.NewObject(graphql.ObjectConfig{ Name: "ArticleConnection", Description: "条件筛选文章列表", Fields: graphql.Fields{ "list": &graphql.Field{Type: graphql.NewList(article), Description: "文章列表"}, "total": &graphql.Field{Type: graphql.Int, Description: "文章总数"}, }, }), Args: graphql.FieldConfigArgument{ "id": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选文章中指定ID的"}, "title": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选文章中含有指定标题的"}, "tags": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选文章中含有指定标签的"}, "create_time": &graphql.ArgumentConfig{Type: graphql.DateTime, Description: "筛选文章中创建时间等于指定值的"}, "update_time": &graphql.ArgumentConfig{Type: graphql.DateTime, Description: "筛选文章中更新时间等于指定值的"}, }, Resolve: func(p graphql.ResolveParams) (interface{}, error) { first := 10 // p.Args["first"] after := 0 // p.Args["after"] fields := strings.Join(get_fields(p.Info.FieldASTs[0].SelectionSet.Selections), ",") var where []string if p.Args["id"] != nil { where = append(where, fmt.Sprintf("id=%d", p.Args["id"])) } if p.Args["title"] != nil { where = append(where, fmt.Sprintf("title='%s'", p.Args["title"])) } // 筛选条件 where_str := strings.Join(where, " AND ") if where_str != "" { where_str = "WHERE " + where_str } var query strings.Builder query.WriteString(fmt.Sprintf("SELECT %s FROM web_article %s LIMIT %d OFFSET %d", fields, where_str, first, after)) // 返回翻页信息 var articles []Article if err := connection.Select(&articles, query.String()); err != nil { fmt.Println("获取文章列表失败", err) return nil, err } return map[string]interface{}{ "list": articles, "total": 0, }, nil }, }, "tags": &graphql.Field{ Name: "tags", Description: "标签列表", Type: graphql.NewObject(graphql.ObjectConfig{ Name: "TagConnection", Description: "条件筛选标签列表", Fields: graphql.Fields{ "list": &graphql.Field{Type: graphql.NewList(graphql.String), Description: "标签列表"}, "total": &graphql.Field{Type: graphql.Int, Description: "标签总数"}, }, }), Args: graphql.FieldConfigArgument{ "first": &graphql.ArgumentConfig{Type: graphql.Int, Description: "翻页参数(傳回清單中的前n個元素)"}, "last": &graphql.ArgumentConfig{Type: graphql.Int, Description: "翻页参数(傳回清單中的最後n個元素)"}, "after": &graphql.ArgumentConfig{Type: graphql.String, Description: "翻页参数(傳回清單中指定遊標之後的元素)"}, "before": &graphql.ArgumentConfig{Type: graphql.String, Description: "翻页参数(傳回清單中指定遊標之前的元素)"}, }, Resolve: func(p graphql.ResolveParams) (interface{}, error) { var tags []string if err := connection.Select(&tags, "SELECT DISTINCT tags FROM web_images LIMIT 10"); err != nil { fmt.Println("获取标签列表失败", err) return nil, err } return map[string]interface{}{ "list": tags, "total": 0, }, nil }, }, "image": &graphql.Field{ Name: "image", Description: "单张图片", Type: image, 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 }, }, }})}) if err != nil { return schema, err } return schema, nil }