223 lines
5.8 KiB
Go
223 lines
5.8 KiB
Go
package api
|
|
|
|
import (
|
|
"fmt"
|
|
"image"
|
|
"log"
|
|
"os"
|
|
"reflect"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
_ "github.com/doug-martin/goqu/v9/dialect/mysql"
|
|
"github.com/graphql-go/graphql"
|
|
"github.com/graphql-go/graphql/language/ast"
|
|
"github.com/spf13/viper"
|
|
"github.com/thoas/go-funk"
|
|
"gorm.io/driver/mysql"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
var db *gorm.DB
|
|
var err error
|
|
|
|
func InitDefault(config *viper.Viper) {
|
|
if db, err = gorm.Open(mysql.Open(fmt.Sprintf(
|
|
"%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
|
config.GetString("mysql.user"),
|
|
config.GetString("mysql.password"),
|
|
config.GetString("mysql.host"),
|
|
config.GetInt("mysql.port"),
|
|
config.GetString("mysql.database"),
|
|
)), &gorm.Config{}); err != nil {
|
|
log.Fatal("failed to connect to database:", err)
|
|
}
|
|
}
|
|
|
|
func ParseToken(token string) (user_id int) {
|
|
if err := db.Table("web_auth").Select("user_id").Where("token = ?", token).Scan(&user_id).Error; err != nil {
|
|
fmt.Println("token解析失败", err)
|
|
}
|
|
return user_id
|
|
}
|
|
|
|
// 定时检查补全颜色字段
|
|
func CheckColorNullRows(offset int) {
|
|
for {
|
|
time.Sleep(1 * time.Second)
|
|
var list []struct {
|
|
ID int
|
|
Content string
|
|
}
|
|
|
|
fmt.Println("跳过的行数:", offset)
|
|
if err := db.Table("web_images").Select("id", "content").Where("article_category_top_id = 22").Where("color_0_r IS NULL").Offset(offset).Limit(100).Scan(&list).Error; err != nil {
|
|
fmt.Println("定时检查补全颜色字段查询失败", err)
|
|
continue
|
|
}
|
|
for index, item := range list {
|
|
matches := regexp.MustCompile(`^https?://image\.gameuiux\.cn/(.*)`).FindStringSubmatch(item.Content)
|
|
if len(matches) < 2 {
|
|
fmt.Println("转换路径失败", index, item.ID, item.Content)
|
|
continue
|
|
}
|
|
// 打开图像文件
|
|
filepath := "oss/" + matches[1]
|
|
file, err := os.Open(filepath)
|
|
if err != nil {
|
|
fmt.Println("打开文件失败", index, item.ID, item.Content, err)
|
|
offset++
|
|
continue
|
|
}
|
|
defer file.Close()
|
|
|
|
// 解码 JPEG 图像
|
|
img, _, err := image.Decode(file)
|
|
if err != nil {
|
|
fmt.Println("解码图像失败", index, item.ID, item.Content, err)
|
|
continue
|
|
}
|
|
|
|
k := 8
|
|
centers, labels := KMeans(extractColors(img), k)
|
|
|
|
// 将聚类中心和颜色数量结合,并按颜色数量降序排序
|
|
type cluster struct {
|
|
center RGB
|
|
count int
|
|
}
|
|
clusters := make([]cluster, k)
|
|
for i := 0; i < k; i++ {
|
|
clusters[i] = cluster{center: centers[i], count: 0}
|
|
}
|
|
// 统计每个聚类的颜色数量
|
|
for _, label := range labels {
|
|
clusters[label].count++
|
|
}
|
|
// 按颜色数量降序排序
|
|
sort.Slice(clusters, func(i, j int) bool {
|
|
return clusters[i].count > clusters[j].count
|
|
})
|
|
// 返回排序后的聚类中心
|
|
sortedCenters := make([]RGB, k)
|
|
for i, c := range clusters {
|
|
sortedCenters[i] = c.center
|
|
}
|
|
|
|
//fmt.Println("聚类后的颜色数量:", clusters)
|
|
|
|
if err := db.Table("web_images").Where("id = ?", item.ID).Updates(map[string]interface{}{
|
|
"color_0_r": sortedCenters[0].R,
|
|
"color_0_g": sortedCenters[0].G,
|
|
"color_0_b": sortedCenters[0].B,
|
|
"color_1_r": sortedCenters[1].R,
|
|
"color_1_g": sortedCenters[1].G,
|
|
"color_1_b": sortedCenters[1].B,
|
|
}).Error; err != nil {
|
|
fmt.Println("更新颜色字段失败", index, item.ID, item.Content, err)
|
|
continue
|
|
}
|
|
fmt.Println("更新颜色索引:", item.ID, filepath)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 获取所有字段名
|
|
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 existField(selections []ast.Selection, name string) bool {
|
|
for _, field := range selections {
|
|
if f, ok := field.(*ast.Field); ok && f.Name.Value == name {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// 将 list 中的字段提取出来用于查询
|
|
func get_fields(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)
|
|
if fieldAST.Name.Value == "text_count" {
|
|
continue
|
|
}
|
|
fields = append(fields, fieldAST.Name.Value)
|
|
}
|
|
}
|
|
}
|
|
return fields
|
|
}
|
|
|
|
var orderType = graphql.NewEnum(graphql.EnumConfig{
|
|
Name: "OrderType",
|
|
Description: "排序类型",
|
|
Values: graphql.EnumValueConfigMap{
|
|
"ASC": &graphql.EnumValueConfig{
|
|
Value: "ASC",
|
|
Description: "升序",
|
|
},
|
|
"DESC": &graphql.EnumValueConfig{
|
|
Value: "DESC",
|
|
Description: "降序",
|
|
},
|
|
},
|
|
})
|