翻页
This commit is contained in:
24
README.md
24
README.md
@@ -4,6 +4,30 @@
|
|||||||
- [x] 提供流媒体服务
|
- [x] 提供流媒体服务
|
||||||
- [x] 点击播放之前不加载视频(减少流量消耗)
|
- [x] 点击播放之前不加载视频(减少流量消耗)
|
||||||
- [x] 使用封面图片替代加载视屏第一帧
|
- [x] 使用封面图片替代加载视屏第一帧
|
||||||
|
- [x] GraphQL 风格API
|
||||||
|
- [ ] 列表翻页
|
||||||
|
|
||||||
|
|
||||||
|
GraphQL
|
||||||
|
```javascript
|
||||||
|
const query = `
|
||||||
|
query ($id: Int!) {
|
||||||
|
article(id: $id) {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
content
|
||||||
|
author {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
通过流媒体服务降低视频文件加载消耗及防止恶意刷流量
|
通过流媒体服务降低视频文件加载消耗及防止恶意刷流量
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/graphql-go/graphql"
|
"github.com/graphql-go/graphql"
|
||||||
"github.com/graphql-go/graphql/language/ast"
|
"github.com/graphql-go/graphql/language/ast"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewSchema(config Config) (graphql.Schema, error) {
|
func NewSchema(config Config) (graphql.Schema, error) {
|
||||||
@@ -52,7 +53,7 @@ func NewSchema(config Config) (graphql.Schema, error) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// 图像中的文字提取 [{"text": "角色选择", "confidence": 0.8484202027320862, "coordinate": [[666.0, 66.0], [908.0, 81.0], [903.0, 174.0], [661.0, 160.0]]}
|
// 图像中的文字提取
|
||||||
text := graphql.NewObject(graphql.ObjectConfig{
|
text := graphql.NewObject(graphql.ObjectConfig{
|
||||||
Name: "Text",
|
Name: "Text",
|
||||||
Fields: graphql.Fields{
|
Fields: graphql.Fields{
|
||||||
@@ -172,26 +173,38 @@ func NewSchema(config Config) (graphql.Schema, error) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
Args: graphql.FieldConfigArgument{
|
Args: graphql.FieldConfigArgument{
|
||||||
"id": &graphql.ArgumentConfig{Type: graphql.Int},
|
"id": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选图像中指定ID的"},
|
||||||
"width": &graphql.ArgumentConfig{Type: graphql.Int},
|
"width": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选图像中指定宽度的"},
|
||||||
"height": &graphql.ArgumentConfig{Type: graphql.Int},
|
"height": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选图像中指定高度的"},
|
||||||
"content": &graphql.ArgumentConfig{Type: graphql.String},
|
"content": &graphql.ArgumentConfig{Type: graphql.String},
|
||||||
"remark": &graphql.ArgumentConfig{Type: graphql.String},
|
"remark": &graphql.ArgumentConfig{Type: graphql.String},
|
||||||
"description": &graphql.ArgumentConfig{Type: graphql.String},
|
"description": &graphql.ArgumentConfig{Type: graphql.String},
|
||||||
"tags": &graphql.ArgumentConfig{Type: graphql.String},
|
"tags": &graphql.ArgumentConfig{Type: graphql.String},
|
||||||
"rank": &graphql.ArgumentConfig{Type: graphql.String},
|
"rank": &graphql.ArgumentConfig{Type: graphql.String},
|
||||||
"text": &graphql.ArgumentConfig{Type: graphql.String}, // 查找图像中的文字
|
"text": &graphql.ArgumentConfig{Type: graphql.String, Description: "筛选图像中含有指定文字的"},
|
||||||
"comment_num": &graphql.ArgumentConfig{Type: graphql.Int},
|
"comment_num": &graphql.ArgumentConfig{Type: graphql.Int},
|
||||||
"praise_count": &graphql.ArgumentConfig{Type: graphql.Int},
|
"praise_count": &graphql.ArgumentConfig{Type: graphql.Int},
|
||||||
"collect_count": &graphql.ArgumentConfig{Type: graphql.Int},
|
"collect_count": &graphql.ArgumentConfig{Type: graphql.Int},
|
||||||
"article_id": &graphql.ArgumentConfig{Type: graphql.Int},
|
"article_id": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选图像中属于指定文章ID的"},
|
||||||
"user_id": &graphql.ArgumentConfig{Type: graphql.Int},
|
"user_id": &graphql.ArgumentConfig{Type: graphql.Int, Description: "筛选图像中属于指定用户ID的"},
|
||||||
"create_time": &graphql.ArgumentConfig{Type: graphql.DateTime},
|
"create_time": &graphql.ArgumentConfig{Type: graphql.DateTime},
|
||||||
"update_time": &graphql.ArgumentConfig{Type: graphql.DateTime},
|
"update_time": &graphql.ArgumentConfig{Type: graphql.DateTime},
|
||||||
"first": &graphql.ArgumentConfig{Type: graphql.Int, DefaultValue: 10}, // 翻页参数
|
"first": &graphql.ArgumentConfig{Type: graphql.Int, Description: "翻页参数(傳回清單中的前n個元素)"},
|
||||||
"after": &graphql.ArgumentConfig{Type: graphql.String, DefaultValue: "0"}, // 翻页参数
|
"last": &graphql.ArgumentConfig{Type: graphql.Int, Description: "翻页参数(傳回清單中的最後n個元素)"},
|
||||||
|
"after": &graphql.ArgumentConfig{Type: graphql.Int, Description: "翻页参数(傳回清單中指定遊標之後的元素)"},
|
||||||
|
"before": &graphql.ArgumentConfig{Type: graphql.Int, Description: "翻页参数(傳回清單中指定遊標之前的元素)"},
|
||||||
},
|
},
|
||||||
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
|
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
|
||||||
|
// 定义参数结构体
|
||||||
|
var args struct {
|
||||||
|
First int
|
||||||
|
Last int
|
||||||
|
After int
|
||||||
|
Before int
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
mapstructure.Decode(p.Args, &args)
|
||||||
|
|
||||||
// 返回字段
|
// 返回字段
|
||||||
var fields []string
|
var fields []string
|
||||||
requestedFields := p.Info.FieldASTs[0].SelectionSet.Selections
|
requestedFields := p.Info.FieldASTs[0].SelectionSet.Selections
|
||||||
@@ -220,8 +233,7 @@ func NewSchema(config Config) (graphql.Schema, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
first := p.Args["first"]
|
|
||||||
after := p.Args["after"]
|
|
||||||
fields_str := strings.Join(fields, ",")
|
fields_str := strings.Join(fields, ",")
|
||||||
|
|
||||||
// 参数到 SQL 格式字符串的映射
|
// 参数到 SQL 格式字符串的映射
|
||||||
@@ -252,10 +264,11 @@ func NewSchema(config Config) (graphql.Schema, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 特殊处理 text 参数
|
// 特殊处理 text 参数
|
||||||
if p.Args["text"] != nil {
|
var id_list []string
|
||||||
id_list := models.ElasticsearchSearch(p.Args["text"].(string)).GetIDList()
|
if args.Text != "" {
|
||||||
|
fmt.Println("args:", args)
|
||||||
|
id_list = models.ElasticsearchSearch(args.Text).GetIDList(args.First, args.Last, args.After, args.Before)
|
||||||
id_list_str := strings.Trim(strings.Join(strings.Fields(fmt.Sprint(id_list)), ","), "[]")
|
id_list_str := strings.Trim(strings.Join(strings.Fields(fmt.Sprint(id_list)), ","), "[]")
|
||||||
fmt.Println("id_list_str:", id_list_str)
|
|
||||||
if id_list_str == "" {
|
if id_list_str == "" {
|
||||||
return map[string]interface{}{
|
return map[string]interface{}{
|
||||||
"list": []Image{},
|
"list": []Image{},
|
||||||
@@ -263,14 +276,15 @@ func NewSchema(config Config) (graphql.Schema, error) {
|
|||||||
"total": 0,
|
"total": 0,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
where = append(where, fmt.Sprintf("id IN (%s)", id_list_str))
|
where = append(where, fmt.Sprintf("id IN (%s) LIMIT %d", id_list_str, len(id_list)))
|
||||||
}
|
}
|
||||||
|
|
||||||
where_str := strings.Join(where, " AND ")
|
where_str := strings.Join(where, " AND ")
|
||||||
|
|
||||||
// 执行查询
|
// 执行查询
|
||||||
var query strings.Builder
|
var query strings.Builder
|
||||||
query.WriteString(fmt.Sprintf("SELECT %s FROM web_images WHERE %s LIMIT %d OFFSET %s", fields_str, where_str, first, after))
|
query.WriteString(fmt.Sprintf("SELECT %s FROM web_images WHERE %s", fields_str, where_str))
|
||||||
|
fmt.Println("query:", query.String())
|
||||||
|
|
||||||
var images ImageList
|
var images ImageList
|
||||||
if err := connection.Select(&images, query.String()); err != nil {
|
if err := connection.Select(&images, query.String()); err != nil {
|
||||||
@@ -278,6 +292,11 @@ func NewSchema(config Config) (graphql.Schema, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 按照 id_list 的顺序重新排序
|
||||||
|
if len(id_list) > 0 {
|
||||||
|
images.SortByIDList(id_list)
|
||||||
|
}
|
||||||
|
|
||||||
// 获取用户信息(如果图像列表不为空且请求字段中包含user)
|
// 获取用户信息(如果图像列表不为空且请求字段中包含user)
|
||||||
if len(images) > 0 && strings.Contains(fields_str, "user") {
|
if len(images) > 0 && strings.Contains(fields_str, "user") {
|
||||||
user_ids_str := images.ToAllUserID().ToString()
|
user_ids_str := images.ToAllUserID().ToString()
|
||||||
|
@@ -3,6 +3,7 @@ package api
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -16,6 +17,20 @@ func (ids IDS) ToString() (str string) {
|
|||||||
|
|
||||||
type ImageList []Image
|
type ImageList []Image
|
||||||
|
|
||||||
|
// 按照ID排序
|
||||||
|
func (image *ImageList) SortByIDList(id_list []string) {
|
||||||
|
var sortedImageList ImageList
|
||||||
|
for _, id := range id_list {
|
||||||
|
id_number, _ := strconv.Atoi(id)
|
||||||
|
for _, image := range *image {
|
||||||
|
if image.ID == id_number {
|
||||||
|
sortedImageList = append(sortedImageList, image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*image = sortedImageList
|
||||||
|
}
|
||||||
|
|
||||||
// 取到所有的文章ID, 去除重复
|
// 取到所有的文章ID, 去除重复
|
||||||
func (images *ImageList) ToAllArticleID() (uniqueIds IDS) {
|
func (images *ImageList) ToAllArticleID() (uniqueIds IDS) {
|
||||||
article_ids := make(map[int]bool)
|
article_ids := make(map[int]bool)
|
||||||
|
35
bin/main.go
35
bin/main.go
@@ -18,7 +18,7 @@ import (
|
|||||||
"git.satori.love/gameui/webp/api"
|
"git.satori.love/gameui/webp/api"
|
||||||
"git.satori.love/gameui/webp/models"
|
"git.satori.love/gameui/webp/models"
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
"github.com/graphql-go/graphql"
|
"github.com/graphql-go/handler"
|
||||||
"github.com/milvus-io/milvus-sdk-go/v2/entity"
|
"github.com/milvus-io/milvus-sdk-go/v2/entity"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
@@ -210,19 +210,24 @@ func main() {
|
|||||||
w.Write([]byte("Hello World!"))
|
w.Write([]byte("Hello World!"))
|
||||||
})
|
})
|
||||||
|
|
||||||
http.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
|
http.Handle("/graphql", handler.New(&handler.Config{
|
||||||
defer LogComponent(time.Now().UnixNano(), r) // 最后打印日志
|
Schema: &schema,
|
||||||
query := r.URL.Query().Get("query")
|
Pretty: true,
|
||||||
params := graphql.Params{Schema: schema, RequestString: query}
|
}))
|
||||||
result := graphql.Do(params)
|
|
||||||
if len(result.Errors) > 0 {
|
//http.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
|
||||||
fmt.Printf("failed to execute graphql operation, errors: %+v", result.Errors)
|
// defer LogComponent(time.Now().UnixNano(), r) // 最后打印日志
|
||||||
http.Error(w, result.Errors[0].Error(), 500)
|
// query := r.URL.Query().Get("query")
|
||||||
return
|
// params := graphql.Params{Schema: schema, RequestString: query}
|
||||||
}
|
// result := graphql.Do(params)
|
||||||
rJSON, _ := json.MarshalIndent(result.Data, "", " ")
|
// if len(result.Errors) > 0 {
|
||||||
w.Write(rJSON)
|
// fmt.Printf("failed to execute graphql operation, errors: %+v", result.Errors)
|
||||||
})
|
// http.Error(w, result.Errors[0].Error(), 500)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// rJSON, _ := json.MarshalIndent(result.Data, "", " ")
|
||||||
|
// w.Write(rJSON)
|
||||||
|
//})
|
||||||
|
|
||||||
http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
|
||||||
defer LogComponent(time.Now().UnixNano(), r) // 最后打印日志
|
defer LogComponent(time.Now().UnixNano(), r) // 最后打印日志
|
||||||
@@ -320,7 +325,7 @@ func main() {
|
|||||||
// 如果是查询 text, 直接从 Elasticsearch 返回结果
|
// 如果是查询 text, 直接从 Elasticsearch 返回结果
|
||||||
var text_ids []string
|
var text_ids []string
|
||||||
if text := QueryConditions("text"); len(text) > 0 {
|
if text := QueryConditions("text"); len(text) > 0 {
|
||||||
text_ids := models.ElasticsearchSearch(strings.Join(text, " ")).GetIDList()
|
text_ids := models.ElasticsearchSearch(strings.Join(text, " ")).GetIDList(0, 0, 0, 0)
|
||||||
if len(text_ids) > 0 {
|
if len(text_ids) > 0 {
|
||||||
conditions.WriteString(fmt.Sprintf(" WHERE id IN (%s)", strings.Trim(strings.Replace(fmt.Sprint(text_ids), " ", ",", -1), "[]")))
|
conditions.WriteString(fmt.Sprintf(" WHERE id IN (%s)", strings.Trim(strings.Replace(fmt.Sprint(text_ids), " ", ",", -1), "[]")))
|
||||||
} else {
|
} else {
|
||||||
|
1
go.mod
1
go.mod
@@ -28,6 +28,7 @@ require (
|
|||||||
github.com/elastic/elastic-transport-go/v8 v8.3.0 // indirect
|
github.com/elastic/elastic-transport-go/v8 v8.3.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
|
github.com/graphql-go/handler v0.2.3 // indirect
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
|
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
2
go.sum
2
go.sum
@@ -172,6 +172,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR
|
|||||||
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/graphql-go/graphql v0.8.1 h1:p7/Ou/WpmulocJeEx7wjQy611rtXGQaAcXGqanuMMgc=
|
github.com/graphql-go/graphql v0.8.1 h1:p7/Ou/WpmulocJeEx7wjQy611rtXGQaAcXGqanuMMgc=
|
||||||
github.com/graphql-go/graphql v0.8.1/go.mod h1:nKiHzRM0qopJEwCITUuIsxk9PlVlwIiiI8pnJEhordQ=
|
github.com/graphql-go/graphql v0.8.1/go.mod h1:nKiHzRM0qopJEwCITUuIsxk9PlVlwIiiI8pnJEhordQ=
|
||||||
|
github.com/graphql-go/handler v0.2.3 h1:CANh8WPnl5M9uA25c2GBhPqJhE53Fg0Iue/fRNla71E=
|
||||||
|
github.com/graphql-go/handler v0.2.3/go.mod h1:leLF6RpV5uZMN1CdImAxuiayrYYhOk33bZciaUGaXeU=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
|
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
|
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
@@ -5,6 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
@@ -55,10 +56,49 @@ type SearchData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取搜索结果的 ID 列表
|
// 获取搜索结果的 ID 列表
|
||||||
func (sd SearchData) GetIDList() (id_list []string) {
|
func (sd SearchData) GetIDList(first, last, after, before int) (id_list []string) {
|
||||||
for _, hit := range sd.Hits.Hits {
|
for _, hit := range sd.Hits.Hits {
|
||||||
id_list = append(id_list, hit.ID)
|
id_list = append(id_list, hit.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果 after 不为 0, 从这个ID开始向后取切片
|
||||||
|
if after != 0 {
|
||||||
|
after_str := fmt.Sprint(after)
|
||||||
|
for i, id := range id_list {
|
||||||
|
if id == after_str {
|
||||||
|
id_list = id_list[i+1:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 before 不为 0, 从这个ID开始向前取切片
|
||||||
|
if before != 0 {
|
||||||
|
before_str := fmt.Sprint(before)
|
||||||
|
for i, id := range id_list {
|
||||||
|
if id == before_str {
|
||||||
|
id_list = id_list[:i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 first 不为 0, 取切片的前 first 个元素
|
||||||
|
if first != 0 {
|
||||||
|
if first > len(id_list) {
|
||||||
|
first = len(id_list)
|
||||||
|
}
|
||||||
|
id_list = id_list[:first]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 last 不为 0, 取切片的后 last 个元素
|
||||||
|
if last != 0 {
|
||||||
|
if last > len(id_list) {
|
||||||
|
last = len(id_list)
|
||||||
|
}
|
||||||
|
id_list = id_list[len(id_list)-last:]
|
||||||
|
}
|
||||||
|
|
||||||
return id_list
|
return id_list
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,13 +120,14 @@ func ElasticsearchSearch(text string) (r SearchData) {
|
|||||||
|
|
||||||
es := elasticsearch_init()
|
es := elasticsearch_init()
|
||||||
|
|
||||||
// 执行查询
|
// 执行查询(最大返回200条)
|
||||||
res, err := es.Search(
|
res, err := es.Search(
|
||||||
es.Search.WithContext(context.Background()),
|
es.Search.WithContext(context.Background()),
|
||||||
es.Search.WithIndex("web_images"),
|
es.Search.WithIndex("web_images"),
|
||||||
es.Search.WithBody(&buf),
|
es.Search.WithBody(&buf),
|
||||||
es.Search.WithTrackTotalHits(true),
|
es.Search.WithTrackTotalHits(true),
|
||||||
es.Search.WithPretty(),
|
es.Search.WithPretty(),
|
||||||
|
es.Search.WithSize(200),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error getting response: %s", err)
|
log.Printf("Error getting response: %s", err)
|
||||||
|
Reference in New Issue
Block a user