package api import ( "fmt" "log" "strings" "git.satori.love/gameui/webp/models" "github.com/graphql-go/graphql" "github.com/graphql-go/graphql/language/ast" "github.com/jmoiron/sqlx" "github.com/mitchellh/mapstructure" ) func NewSchema(config Config) (graphql.Schema, error) { // 打开数据库连接 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) } // 文章的可选字段 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: "文章标题"}, "tags": &graphql.Field{Type: graphql.String, Description: "文章标签"}, "create_time": &graphql.Field{Type: graphql.DateTime, Description: "文章创建时间"}, "update_time": &graphql.Field{Type: graphql.DateTime, Description: "文章更新时间"}, }, }) // 用户的可选字段 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.Float, Description: "用户金币"}, "create_time": &graphql.Field{Type: graphql.DateTime, Description: "用户创建时间"}, "update_time": &graphql.Field{Type: graphql.DateTime, Description: "用户更新时间"}, }, }) // 图像中的文字提取 text := 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: "文字坐标"}, }, }) // 图像的可选字段 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: "图像等级"}, "text": &graphql.Field{Type: graphql.NewList(text), 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: "图像更新时间"}, "user": &graphql.Field{Type: user, Description: "图像所属用户"}, "article": &graphql.Field{Type: article, Description: "图像所属文章"}, }, }) schema, err := graphql.NewSchema(graphql.SchemaConfig{Query: graphql.NewObject(graphql.ObjectConfig{Name: "RootQuery", Fields: graphql.Fields{ "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 fields []string requestedFields := p.Info.FieldASTs[0].SelectionSet.Selections for _, field := range requestedFields { fieldAST, ok := field.(*ast.Field) if ok { switch fieldAST.Name.Value { case "list": for _, field := range fieldAST.SelectionSet.Selections { fieldAST, ok := field.(*ast.Field) if ok { fields = append(fields, fieldAST.Name.Value) } } case "next": fmt.Println("next") case "text": fmt.Println("text") default: fmt.Println(fieldAST.Name.Value) } } } first := p.Args["first"] after := p.Args["after"] fields_str := strings.Join(fields, ",") 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 query.WriteString(fmt.Sprintf("SELECT %s FROM web_member %s LIMIT %d OFFSET %s", fields_str, where_str, first, after)) // 返回翻页信息 var users []User if err := connection.Select(&users, query.String()); err != nil { fmt.Println("获取用户列表失败", err) return nil, err } return map[string]interface{}{ "list": users, "total": 0, }, 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{ "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: "筛选图像中指定高度的"}, "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: "筛选图像中含有指定文字的"}, "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的"}, "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 args struct { First int Last int After string Before string Text string Similar int } mapstructure.Decode(p.Args, &args) // 处理要求返回的字段 var fields []string requestedFields := p.Info.FieldASTs[0].SelectionSet.Selections for _, field := range requestedFields { fieldAST, ok := field.(*ast.Field) if ok { switch fieldAST.Name.Value { case "list": for _, field := range fieldAST.SelectionSet.Selections { fieldAST, ok := field.(*ast.Field) if ok { switch fieldAST.Name.Value { case "user": fields = append(fields, "user_id") case "article": fields = append(fields, "article_id") default: fields = append(fields, fieldAST.Name.Value) } } } case "total": fmt.Println("total") default: fmt.Println(fieldAST.Name.Value) } } } fields_str := strings.Join(fields, ",") // 参数到 SQL 格式字符串的映射 var argToSQLFormat = map[string]string{ "id": "id=%d", "width": "width=%d", "height": "height=%d", "content": "content='%s'", "remark": "remark='%s'", "description": "description='%s'", "tags": "tags='%s'", "rank": "rank='%s'", "comment_num": "comment_num=%d", "praise_count": "praise_count=%d", "collect_count": "collect_count=%d", "article_id": "article_id=%d", "user_id": "user_id=%d", "create_time": "create_time='%s'", "update_time": "update_time='%s'", } // 筛选条件 var where []string for arg, format := range argToSQLFormat { if p.Args[arg] != nil { where = append(where, fmt.Sprintf(format, p.Args[arg])) } } var id_list []string // 特殊处理 similar 参数 if args.Similar != 0 { fmt.Println("similar:", args.Similar) id_list := models.GetSimilarImagesIdList(args.Similar, 200) fmt.Println("ids:", id_list) ids_str := strings.Trim(strings.Join(strings.Fields(fmt.Sprint(id_list)), ","), "[]") if ids_str == "" { return map[string]interface{}{"list": []Image{}, "total": 0}, nil } where = append(where, fmt.Sprintf("id IN (%s) LIMIT %d", ids_str, len(id_list))) } // 特殊处理 text 参数 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}, }, }, }, }, "sort": []string{ "_score", }, "from": 0, "size": 200, }) if err != nil { fmt.Println("ZincSearch 获取图像列表失败", err) return nil, err } id_list = resp.ToIDList(args.First, args.Last, args.After, args.Before) id_list_str := strings.Trim(strings.Join(strings.Fields(fmt.Sprint(id_list)), ","), "[]") if id_list_str == "" { return map[string]interface{}{ "list": []Image{}, "total": 0, }, nil } where = append(where, fmt.Sprintf("id IN (%s) LIMIT %d", id_list_str, len(id_list))) } where_str := strings.Join(where, " AND ") // 执行查询 var query strings.Builder query.WriteString(fmt.Sprintf("SELECT %s FROM web_images WHERE %s", fields_str, where_str)) fmt.Println("query:", query.String()) var images ImageList if err := connection.Select(&images, query.String()); err != nil { fmt.Println("获取图像列表失败", err) return nil, err } // 按照 id_list 的顺序重新排序 if len(id_list) > 0 { images.SortByIDList(id_list) } // 获取用户信息(如果图像列表不为空且请求字段中包含user) if len(images) > 0 && strings.Contains(fields_str, "user") { user_ids_str := images.ToAllUserID().ToString() var users []User if err := connection.Select(&users, fmt.Sprintf("SELECT id,user_name,avatar,rank,create_time,update_time FROM web_member WHERE id IN (%s)", user_ids_str)); err != nil { fmt.Println("获取用户列表失败", err) return nil, err } // 将用户信息与图像信息关联 images.SetUser(users) } // 获取文章信息(如果图像列表不为空且请求字段中包含article) if len(images) > 0 && strings.Contains(fields_str, "article") { article_ids_str := images.ToAllArticleID().ToString() var articles []Article if err := connection.Select(&articles, fmt.Sprintf("SELECT id,title,tags,create_time,update_time FROM web_article WHERE id IN (%s)", article_ids_str)); err != nil { fmt.Println("获取文章列表失败", err) return nil, err } // 将文章信息与图像信息关联 images.SetArticle(articles) } return map[string]interface{}{ "list": images, "total": 0, }, 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) { var fields []string requestedFields := p.Info.FieldASTs[0].SelectionSet.Selections for _, field := range requestedFields { fieldAST, ok := field.(*ast.Field) if ok { switch fieldAST.Name.Value { case "list": for _, field := range fieldAST.SelectionSet.Selections { fieldAST, ok := field.(*ast.Field) if ok { fields = append(fields, fieldAST.Name.Value) } } case "total": fmt.Println("total") default: fmt.Println(fieldAST.Name.Value) } } } first := p.Args["first"] after := p.Args["after"] fields_str := strings.Join(fields, ",") 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 %s", fields_str, 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 }, }, }})}) if err != nil { return schema, err } return schema, nil }