✨ add face recognition
This commit is contained in:
944
.idea/GOHCache.xml
generated
944
.idea/GOHCache.xml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,71 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package auth;
|
|
||||||
option go_package = "./pb";
|
|
||||||
|
|
||||||
message AccountLoginRequest {
|
|
||||||
string account = 1;
|
|
||||||
string password = 2;
|
|
||||||
bool auto_login = 3;
|
|
||||||
int64 angle = 4;
|
|
||||||
string key = 5;
|
|
||||||
}
|
|
||||||
message PhoneLoginRequest {
|
|
||||||
string phone = 1;
|
|
||||||
int64 captcha = 2;
|
|
||||||
bool auto_login = 3;
|
|
||||||
}
|
|
||||||
message ResetPasswordRequest {
|
|
||||||
string phone = 1;
|
|
||||||
string captcha = 2;
|
|
||||||
string password = 3;
|
|
||||||
string repassword = 4;
|
|
||||||
}
|
|
||||||
message WechatOffiaccountLoginRequest {
|
|
||||||
string openid = 1;
|
|
||||||
string client_id = 2;
|
|
||||||
}
|
|
||||||
message GetWechatQrcodeRequest {
|
|
||||||
string client_id = 1;
|
|
||||||
}
|
|
||||||
message LoginResponse {
|
|
||||||
string access_token = 1;
|
|
||||||
int64 expire_at = 2;
|
|
||||||
string uid = 3;
|
|
||||||
string username = 4;
|
|
||||||
string nickname = 5;
|
|
||||||
string avatar = 6;
|
|
||||||
int64 status = 7;
|
|
||||||
}
|
|
||||||
message ResetPasswordResponse {
|
|
||||||
bool success = 1;
|
|
||||||
}
|
|
||||||
message GetWechatQrcodeResponse {
|
|
||||||
string qrcode = 1;
|
|
||||||
}
|
|
||||||
// The LoginService service definition.
|
|
||||||
service LoginService{
|
|
||||||
// AccountLogin
|
|
||||||
rpc AccountLogin (AccountLoginRequest) returns (LoginResponse);
|
|
||||||
// PhoneLogin
|
|
||||||
rpc PhoneLogin (AccountLoginRequest) returns (LoginResponse);
|
|
||||||
// ResetPassword
|
|
||||||
rpc ResetPassword (ResetPasswordRequest) returns (ResetPasswordResponse);
|
|
||||||
// WechatOffiaccountLogin
|
|
||||||
rpc WechatOffiaccountLogin (WechatOffiaccountLoginRequest) returns (LoginResponse);
|
|
||||||
// GetWechatOffiaccountQrcode
|
|
||||||
rpc GetWechatOffiaccountQrcode (GetWechatQrcodeRequest) returns (GetWechatQrcodeResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The TokenService service definition.
|
|
||||||
message TokenRequest {
|
|
||||||
string uid = 1;
|
|
||||||
}
|
|
||||||
message RefreshTokenResponse {
|
|
||||||
string access_token = 1;
|
|
||||||
int64 expire_at = 2;
|
|
||||||
}
|
|
||||||
service TokenService{
|
|
||||||
// RefreshToken
|
|
||||||
rpc RefreshToken (TokenRequest) returns (RefreshTokenResponse);
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
package rpc
|
|
||||||
|
|
||||||
//go:generate goctl rpc protoc ai.proto --go_out=. --go-grpc_out=. --zrpc_out=. --client=true -m --style=go_zero
|
|
107
app/aisvc/model/mysql/generate/generate.go
Normal file
107
app/aisvc/model/mysql/generate/generate.go
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gorm.io/driver/mysql"
|
||||||
|
"gorm.io/gen"
|
||||||
|
"gorm.io/gen/field"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
const MySQLDSN = "root:LDQ20020618xxx@tcp(1.95.0.111:3306)/schisandra-cloud-album?charset=utf8mb4&parseTime=True&loc=Local"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// 连接数据库
|
||||||
|
db, err := gorm.Open(mysql.Open(MySQLDSN))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
path := filepath.Join(dir, "app/aisvc/model/mysql/", "query")
|
||||||
|
// 生成实例
|
||||||
|
g := gen.NewGenerator(gen.Config{
|
||||||
|
// 相对执行`go run`时的路径, 会自动创建目录
|
||||||
|
OutPath: path,
|
||||||
|
// 生成的文件名,默认gen.go
|
||||||
|
OutFile: "gen.go",
|
||||||
|
// 生成DAO代码的包名,默认是:model
|
||||||
|
ModelPkgPath: "model",
|
||||||
|
// 是否为DAO包生成单元测试代码,默认:false
|
||||||
|
WithUnitTest: false,
|
||||||
|
|
||||||
|
// WithDefaultQuery 生成默认查询结构体(作为全局变量使用), 即`Q`结构体和其字段(各表模型)
|
||||||
|
// WithoutContext 生成没有context调用限制的代码供查询
|
||||||
|
// WithQueryInterface 生成interface形式的查询代码(可导出), 如`Where()`方法返回的就是一个可导出的接口类型
|
||||||
|
Mode: gen.WithDefaultQuery | gen.WithQueryInterface | gen.WithoutContext,
|
||||||
|
|
||||||
|
// 表字段可为 null 值时, 对应结体字段使用指针类型
|
||||||
|
FieldNullable: false, // generate pointer when field is nullable
|
||||||
|
|
||||||
|
// 表字段默认值与模型结构体字段零值不一致的字段, 在插入数据时需要赋值该字段值为零值的, 结构体字段须是指针类型才能成功, 即`FieldCoverable:true`配置下生成的结构体字段.
|
||||||
|
// 因为在插入时遇到字段为零值的会被GORM赋予默认值. 如字段`age`表默认值为10, 即使你显式设置为0最后也会被GORM设为10提交.
|
||||||
|
// 如果该字段没有上面提到的插入时赋零值的特殊需要, 则字段为非指针类型使用起来会比较方便.
|
||||||
|
FieldCoverable: true,
|
||||||
|
|
||||||
|
// 模型结构体字段的数字类型的符号表示是否与表字段的一致, `false`指示都用有符号类型
|
||||||
|
FieldSignable: false,
|
||||||
|
// 生成 gorm 标签的字段索引属性
|
||||||
|
FieldWithIndexTag: true,
|
||||||
|
// 生成 gorm 标签的字段类型属性
|
||||||
|
FieldWithTypeTag: true,
|
||||||
|
})
|
||||||
|
// 设置目标 db
|
||||||
|
g.UseDB(db)
|
||||||
|
|
||||||
|
// 自定义字段的数据类型
|
||||||
|
// 统一数字类型为int64,兼容protobuf
|
||||||
|
dataMap := map[string]func(columnType gorm.ColumnType) (dataType string){
|
||||||
|
"tinyint": func(columnType gorm.ColumnType) (dataType string) { return "int64" },
|
||||||
|
"smallint": func(columnType gorm.ColumnType) (dataType string) { return "int64" },
|
||||||
|
"mediumint": func(columnType gorm.ColumnType) (dataType string) { return "int64" },
|
||||||
|
"bigint": func(columnType gorm.ColumnType) (dataType string) { return "int64" },
|
||||||
|
"int": func(columnType gorm.ColumnType) (dataType string) { return "int64" },
|
||||||
|
}
|
||||||
|
// 要先于`ApplyBasic`执行
|
||||||
|
g.WithDataTypeMap(dataMap)
|
||||||
|
|
||||||
|
// 自定义模型结体字段的标签
|
||||||
|
// 将特定字段名的 json 标签加上`string`属性,即 MarshalJSON 时该字段由数字类型转成字符串类型
|
||||||
|
jsonField := gen.FieldJSONTagWithNS(func(columnName string) (tagContent string) {
|
||||||
|
toStringField := `id, `
|
||||||
|
if strings.Contains(toStringField, columnName) {
|
||||||
|
return columnName + ",string"
|
||||||
|
}
|
||||||
|
return columnName
|
||||||
|
})
|
||||||
|
// 将非默认字段名的字段定义为自动时间戳和软删除字段;
|
||||||
|
// 自动时间戳默认字段名为:`updated_at`、`created_at, 表字段数据类型为: INT 或 DATETIME
|
||||||
|
// 软删除默认字段名为:`deleted_at`, 表字段数据类型为: DATETIME
|
||||||
|
idField := gen.FieldGORMTag("id", func(tag field.GormTag) field.GormTag {
|
||||||
|
return tag.Append("primary_key")
|
||||||
|
})
|
||||||
|
autoUpdateTimeField := gen.FieldGORMTag("updated_at", func(tag field.GormTag) field.GormTag {
|
||||||
|
return tag.Append("autoUpdateTime")
|
||||||
|
})
|
||||||
|
autoCreateTimeField := gen.FieldGORMTag("created_at", func(tag field.GormTag) field.GormTag {
|
||||||
|
return tag.Append("autoCreateTime")
|
||||||
|
})
|
||||||
|
softDeleteField := gen.FieldType("delete_at", "gorm.DeletedAt")
|
||||||
|
versionField := gen.FieldType("version", "optimisticlock.Version")
|
||||||
|
// 模型自定义选项组
|
||||||
|
fieldOpts := []gen.ModelOpt{jsonField, idField, autoUpdateTimeField, autoCreateTimeField, softDeleteField, versionField}
|
||||||
|
|
||||||
|
// 创建全部模型文件, 并覆盖前面创建的同名模型
|
||||||
|
model := g.GenerateModel("sca_storage_face", fieldOpts...)
|
||||||
|
|
||||||
|
g.ApplyBasic(model)
|
||||||
|
|
||||||
|
g.Execute()
|
||||||
|
}
|
31
app/aisvc/model/mysql/model/sca_storage_face.gen.go
Normal file
31
app/aisvc/model/mysql/model/sca_storage_face.gen.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
const TableNameScaStorageFace = "sca_storage_face"
|
||||||
|
|
||||||
|
// ScaStorageFace 人脸特征向量表
|
||||||
|
type ScaStorageFace struct {
|
||||||
|
ID int64 `gorm:"column:id;type:bigint(20);primaryKey;autoIncrement:true;comment:主键;primary_key" json:"id,string"` // 主键
|
||||||
|
UserID string `gorm:"column:user_id;type:varchar(50);comment:用户ID" json:"user_id"` // 用户ID
|
||||||
|
FaceName string `gorm:"column:face_name;type:varchar(255);comment:人脸名称" json:"face_name"` // 人脸名称
|
||||||
|
FaceVector string `gorm:"column:face_vector;type:json;comment:人脸特征向量" json:"face_vector"` // 人脸特征向量
|
||||||
|
FaceImagePath string `gorm:"column:face_image_path;type:varchar(255);comment:人脸图像路径" json:"face_image_path"` // 人脸图像路径
|
||||||
|
FaceType string `gorm:"column:face_type;type:varchar(50);comment:人脸类型标识" json:"face_type"` // 人脸类型标识
|
||||||
|
CreatedAt time.Time `gorm:"column:created_at;type:timestamp;autoCreateTime;comment:创建时间" json:"created_at"` // 创建时间
|
||||||
|
UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp;autoUpdateTime;comment:更新时间" json:"updated_at"` // 更新时间
|
||||||
|
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp;comment:删除时间" json:"deleted_at"` // 删除时间
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName ScaStorageFace's table name
|
||||||
|
func (*ScaStorageFace) TableName() string {
|
||||||
|
return TableNameScaStorageFace
|
||||||
|
}
|
76
app/aisvc/model/mysql/mysql.go
Normal file
76
app/aisvc/model/mysql/mysql.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"schisandra-album-cloud-microservices/app/aisvc/model/mysql/model"
|
||||||
|
"schisandra-album-cloud-microservices/app/aisvc/model/mysql/query"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/asjdf/gorm-cache/cache"
|
||||||
|
"github.com/asjdf/gorm-cache/config"
|
||||||
|
"github.com/asjdf/gorm-cache/storage"
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
|
"gorm.io/driver/mysql"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewMySQL(url string, maxOpenConn int, maxIdleConn int, client *redis.Client) (*gorm.DB, *query.Query) {
|
||||||
|
db, err := gorm.Open(mysql.Open(url), &gorm.Config{
|
||||||
|
SkipDefaultTransaction: true,
|
||||||
|
PrepareStmt: true,
|
||||||
|
Logger: logger.New(
|
||||||
|
log.New(os.Stdout, "\r\n", log.LstdFlags),
|
||||||
|
logger.Config{
|
||||||
|
SlowThreshold: time.Second, // 慢sql日志
|
||||||
|
LogLevel: logger.Error, // 级别
|
||||||
|
Colorful: true, // 颜色
|
||||||
|
IgnoreRecordNotFoundError: true, // 忽略RecordNotFoundError
|
||||||
|
ParameterizedQueries: true, // 格式化SQL语句
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
sqlDB, err := db.DB()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
sqlDB.SetMaxOpenConns(maxOpenConn)
|
||||||
|
sqlDB.SetMaxIdleConns(maxIdleConn)
|
||||||
|
useDB := query.Use(db)
|
||||||
|
// migrate
|
||||||
|
Migrate(db)
|
||||||
|
// cache
|
||||||
|
gormCache, err := cache.NewGorm2Cache(&config.CacheConfig{
|
||||||
|
CacheLevel: config.CacheLevelAll,
|
||||||
|
CacheStorage: storage.NewRedis(&storage.RedisStoreConfig{
|
||||||
|
KeyPrefix: "cache",
|
||||||
|
Client: client,
|
||||||
|
}),
|
||||||
|
InvalidateWhenUpdate: true, // when you create/update/delete objects, invalidate cache
|
||||||
|
CacheTTL: 10000, // 5000 ms
|
||||||
|
CacheMaxItemCnt: 0, // if length of objects retrieved one single time
|
||||||
|
AsyncWrite: true, // async write to cache
|
||||||
|
DebugMode: false,
|
||||||
|
DisableCachePenetrationProtect: true, // disable cache penetration protect
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = db.Use(gormCache)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return db, useDB
|
||||||
|
}
|
||||||
|
|
||||||
|
func Migrate(db *gorm.DB) {
|
||||||
|
err := db.AutoMigrate(
|
||||||
|
&model.ScaStorageFace{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
103
app/aisvc/model/mysql/query/gen.go
Normal file
103
app/aisvc/model/mysql/query/gen.go
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package query
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"gorm.io/gen"
|
||||||
|
|
||||||
|
"gorm.io/plugin/dbresolver"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Q = new(Query)
|
||||||
|
ScaStorageFace *scaStorageFace
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
|
||||||
|
*Q = *Use(db, opts...)
|
||||||
|
ScaStorageFace = &Q.ScaStorageFace
|
||||||
|
}
|
||||||
|
|
||||||
|
func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
|
||||||
|
return &Query{
|
||||||
|
db: db,
|
||||||
|
ScaStorageFace: newScaStorageFace(db, opts...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Query struct {
|
||||||
|
db *gorm.DB
|
||||||
|
|
||||||
|
ScaStorageFace scaStorageFace
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Query) Available() bool { return q.db != nil }
|
||||||
|
|
||||||
|
func (q *Query) clone(db *gorm.DB) *Query {
|
||||||
|
return &Query{
|
||||||
|
db: db,
|
||||||
|
ScaStorageFace: q.ScaStorageFace.clone(db),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Query) ReadDB() *Query {
|
||||||
|
return q.ReplaceDB(q.db.Clauses(dbresolver.Read))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Query) WriteDB() *Query {
|
||||||
|
return q.ReplaceDB(q.db.Clauses(dbresolver.Write))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Query) ReplaceDB(db *gorm.DB) *Query {
|
||||||
|
return &Query{
|
||||||
|
db: db,
|
||||||
|
ScaStorageFace: q.ScaStorageFace.replaceDB(db),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type queryCtx struct {
|
||||||
|
ScaStorageFace IScaStorageFaceDo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Query) WithContext(ctx context.Context) *queryCtx {
|
||||||
|
return &queryCtx{
|
||||||
|
ScaStorageFace: q.ScaStorageFace.WithContext(ctx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Query) Transaction(fc func(tx *Query) error, opts ...*sql.TxOptions) error {
|
||||||
|
return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.clone(tx)) }, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Query) Begin(opts ...*sql.TxOptions) *QueryTx {
|
||||||
|
tx := q.db.Begin(opts...)
|
||||||
|
return &QueryTx{Query: q.clone(tx), Error: tx.Error}
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryTx struct {
|
||||||
|
*Query
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *QueryTx) Commit() error {
|
||||||
|
return q.db.Commit().Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *QueryTx) Rollback() error {
|
||||||
|
return q.db.Rollback().Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *QueryTx) SavePoint(name string) error {
|
||||||
|
return q.db.SavePoint(name).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *QueryTx) RollbackTo(name string) error {
|
||||||
|
return q.db.RollbackTo(name).Error
|
||||||
|
}
|
413
app/aisvc/model/mysql/query/sca_storage_face.gen.go
Normal file
413
app/aisvc/model/mysql/query/sca_storage_face.gen.go
Normal file
@@ -0,0 +1,413 @@
|
|||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package query
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
|
"gorm.io/gorm/schema"
|
||||||
|
|
||||||
|
"gorm.io/gen"
|
||||||
|
"gorm.io/gen/field"
|
||||||
|
|
||||||
|
"gorm.io/plugin/dbresolver"
|
||||||
|
|
||||||
|
"schisandra-album-cloud-microservices/app/aisvc/model/mysql/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newScaStorageFace(db *gorm.DB, opts ...gen.DOOption) scaStorageFace {
|
||||||
|
_scaStorageFace := scaStorageFace{}
|
||||||
|
|
||||||
|
_scaStorageFace.scaStorageFaceDo.UseDB(db, opts...)
|
||||||
|
_scaStorageFace.scaStorageFaceDo.UseModel(&model.ScaStorageFace{})
|
||||||
|
|
||||||
|
tableName := _scaStorageFace.scaStorageFaceDo.TableName()
|
||||||
|
_scaStorageFace.ALL = field.NewAsterisk(tableName)
|
||||||
|
_scaStorageFace.ID = field.NewInt64(tableName, "id")
|
||||||
|
_scaStorageFace.UserID = field.NewString(tableName, "user_id")
|
||||||
|
_scaStorageFace.FaceName = field.NewString(tableName, "face_name")
|
||||||
|
_scaStorageFace.FaceVector = field.NewString(tableName, "face_vector")
|
||||||
|
_scaStorageFace.FaceImagePath = field.NewString(tableName, "face_image_path")
|
||||||
|
_scaStorageFace.FaceType = field.NewString(tableName, "face_type")
|
||||||
|
_scaStorageFace.CreatedAt = field.NewTime(tableName, "created_at")
|
||||||
|
_scaStorageFace.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||||
|
_scaStorageFace.DeletedAt = field.NewField(tableName, "deleted_at")
|
||||||
|
|
||||||
|
_scaStorageFace.fillFieldMap()
|
||||||
|
|
||||||
|
return _scaStorageFace
|
||||||
|
}
|
||||||
|
|
||||||
|
// scaStorageFace 人脸特征向量表
|
||||||
|
type scaStorageFace struct {
|
||||||
|
scaStorageFaceDo
|
||||||
|
|
||||||
|
ALL field.Asterisk
|
||||||
|
ID field.Int64 // 主键
|
||||||
|
UserID field.String // 用户ID
|
||||||
|
FaceName field.String // 人脸名称
|
||||||
|
FaceVector field.String // 人脸特征向量
|
||||||
|
FaceImagePath field.String // 人脸图像路径
|
||||||
|
FaceType field.String // 人脸类型标识
|
||||||
|
CreatedAt field.Time // 创建时间
|
||||||
|
UpdatedAt field.Time // 更新时间
|
||||||
|
DeletedAt field.Field // 删除时间
|
||||||
|
|
||||||
|
fieldMap map[string]field.Expr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFace) Table(newTableName string) *scaStorageFace {
|
||||||
|
s.scaStorageFaceDo.UseTable(newTableName)
|
||||||
|
return s.updateTableName(newTableName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFace) As(alias string) *scaStorageFace {
|
||||||
|
s.scaStorageFaceDo.DO = *(s.scaStorageFaceDo.As(alias).(*gen.DO))
|
||||||
|
return s.updateTableName(alias)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *scaStorageFace) updateTableName(table string) *scaStorageFace {
|
||||||
|
s.ALL = field.NewAsterisk(table)
|
||||||
|
s.ID = field.NewInt64(table, "id")
|
||||||
|
s.UserID = field.NewString(table, "user_id")
|
||||||
|
s.FaceName = field.NewString(table, "face_name")
|
||||||
|
s.FaceVector = field.NewString(table, "face_vector")
|
||||||
|
s.FaceImagePath = field.NewString(table, "face_image_path")
|
||||||
|
s.FaceType = field.NewString(table, "face_type")
|
||||||
|
s.CreatedAt = field.NewTime(table, "created_at")
|
||||||
|
s.UpdatedAt = field.NewTime(table, "updated_at")
|
||||||
|
s.DeletedAt = field.NewField(table, "deleted_at")
|
||||||
|
|
||||||
|
s.fillFieldMap()
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *scaStorageFace) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||||
|
_f, ok := s.fieldMap[fieldName]
|
||||||
|
if !ok || _f == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
_oe, ok := _f.(field.OrderExpr)
|
||||||
|
return _oe, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *scaStorageFace) fillFieldMap() {
|
||||||
|
s.fieldMap = make(map[string]field.Expr, 9)
|
||||||
|
s.fieldMap["id"] = s.ID
|
||||||
|
s.fieldMap["user_id"] = s.UserID
|
||||||
|
s.fieldMap["face_name"] = s.FaceName
|
||||||
|
s.fieldMap["face_vector"] = s.FaceVector
|
||||||
|
s.fieldMap["face_image_path"] = s.FaceImagePath
|
||||||
|
s.fieldMap["face_type"] = s.FaceType
|
||||||
|
s.fieldMap["created_at"] = s.CreatedAt
|
||||||
|
s.fieldMap["updated_at"] = s.UpdatedAt
|
||||||
|
s.fieldMap["deleted_at"] = s.DeletedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFace) clone(db *gorm.DB) scaStorageFace {
|
||||||
|
s.scaStorageFaceDo.ReplaceConnPool(db.Statement.ConnPool)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFace) replaceDB(db *gorm.DB) scaStorageFace {
|
||||||
|
s.scaStorageFaceDo.ReplaceDB(db)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
type scaStorageFaceDo struct{ gen.DO }
|
||||||
|
|
||||||
|
type IScaStorageFaceDo interface {
|
||||||
|
gen.SubQuery
|
||||||
|
Debug() IScaStorageFaceDo
|
||||||
|
WithContext(ctx context.Context) IScaStorageFaceDo
|
||||||
|
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
|
||||||
|
ReplaceDB(db *gorm.DB)
|
||||||
|
ReadDB() IScaStorageFaceDo
|
||||||
|
WriteDB() IScaStorageFaceDo
|
||||||
|
As(alias string) gen.Dao
|
||||||
|
Session(config *gorm.Session) IScaStorageFaceDo
|
||||||
|
Columns(cols ...field.Expr) gen.Columns
|
||||||
|
Clauses(conds ...clause.Expression) IScaStorageFaceDo
|
||||||
|
Not(conds ...gen.Condition) IScaStorageFaceDo
|
||||||
|
Or(conds ...gen.Condition) IScaStorageFaceDo
|
||||||
|
Select(conds ...field.Expr) IScaStorageFaceDo
|
||||||
|
Where(conds ...gen.Condition) IScaStorageFaceDo
|
||||||
|
Order(conds ...field.Expr) IScaStorageFaceDo
|
||||||
|
Distinct(cols ...field.Expr) IScaStorageFaceDo
|
||||||
|
Omit(cols ...field.Expr) IScaStorageFaceDo
|
||||||
|
Join(table schema.Tabler, on ...field.Expr) IScaStorageFaceDo
|
||||||
|
LeftJoin(table schema.Tabler, on ...field.Expr) IScaStorageFaceDo
|
||||||
|
RightJoin(table schema.Tabler, on ...field.Expr) IScaStorageFaceDo
|
||||||
|
Group(cols ...field.Expr) IScaStorageFaceDo
|
||||||
|
Having(conds ...gen.Condition) IScaStorageFaceDo
|
||||||
|
Limit(limit int) IScaStorageFaceDo
|
||||||
|
Offset(offset int) IScaStorageFaceDo
|
||||||
|
Count() (count int64, err error)
|
||||||
|
Scopes(funcs ...func(gen.Dao) gen.Dao) IScaStorageFaceDo
|
||||||
|
Unscoped() IScaStorageFaceDo
|
||||||
|
Create(values ...*model.ScaStorageFace) error
|
||||||
|
CreateInBatches(values []*model.ScaStorageFace, batchSize int) error
|
||||||
|
Save(values ...*model.ScaStorageFace) error
|
||||||
|
First() (*model.ScaStorageFace, error)
|
||||||
|
Take() (*model.ScaStorageFace, error)
|
||||||
|
Last() (*model.ScaStorageFace, error)
|
||||||
|
Find() ([]*model.ScaStorageFace, error)
|
||||||
|
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.ScaStorageFace, err error)
|
||||||
|
FindInBatches(result *[]*model.ScaStorageFace, batchSize int, fc func(tx gen.Dao, batch int) error) error
|
||||||
|
Pluck(column field.Expr, dest interface{}) error
|
||||||
|
Delete(...*model.ScaStorageFace) (info gen.ResultInfo, err error)
|
||||||
|
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
|
||||||
|
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
|
||||||
|
Updates(value interface{}) (info gen.ResultInfo, err error)
|
||||||
|
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
|
||||||
|
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
|
||||||
|
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
|
||||||
|
UpdateFrom(q gen.SubQuery) gen.Dao
|
||||||
|
Attrs(attrs ...field.AssignExpr) IScaStorageFaceDo
|
||||||
|
Assign(attrs ...field.AssignExpr) IScaStorageFaceDo
|
||||||
|
Joins(fields ...field.RelationField) IScaStorageFaceDo
|
||||||
|
Preload(fields ...field.RelationField) IScaStorageFaceDo
|
||||||
|
FirstOrInit() (*model.ScaStorageFace, error)
|
||||||
|
FirstOrCreate() (*model.ScaStorageFace, error)
|
||||||
|
FindByPage(offset int, limit int) (result []*model.ScaStorageFace, count int64, err error)
|
||||||
|
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
|
||||||
|
Scan(result interface{}) (err error)
|
||||||
|
Returning(value interface{}, columns ...string) IScaStorageFaceDo
|
||||||
|
UnderlyingDB() *gorm.DB
|
||||||
|
schema.Tabler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Debug() IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Debug())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) WithContext(ctx context.Context) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.WithContext(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) ReadDB() IScaStorageFaceDo {
|
||||||
|
return s.Clauses(dbresolver.Read)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) WriteDB() IScaStorageFaceDo {
|
||||||
|
return s.Clauses(dbresolver.Write)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Session(config *gorm.Session) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Session(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Clauses(conds ...clause.Expression) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Clauses(conds...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Returning(value interface{}, columns ...string) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Returning(value, columns...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Not(conds ...gen.Condition) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Not(conds...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Or(conds ...gen.Condition) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Or(conds...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Select(conds ...field.Expr) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Select(conds...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Where(conds ...gen.Condition) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Where(conds...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Order(conds ...field.Expr) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Order(conds...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Distinct(cols ...field.Expr) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Distinct(cols...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Omit(cols ...field.Expr) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Omit(cols...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Join(table schema.Tabler, on ...field.Expr) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Join(table, on...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) LeftJoin(table schema.Tabler, on ...field.Expr) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.LeftJoin(table, on...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) RightJoin(table schema.Tabler, on ...field.Expr) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.RightJoin(table, on...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Group(cols ...field.Expr) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Group(cols...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Having(conds ...gen.Condition) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Having(conds...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Limit(limit int) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Limit(limit))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Offset(offset int) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Offset(offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Scopes(funcs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Unscoped() IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Unscoped())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Create(values ...*model.ScaStorageFace) error {
|
||||||
|
if len(values) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return s.DO.Create(values)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) CreateInBatches(values []*model.ScaStorageFace, batchSize int) error {
|
||||||
|
return s.DO.CreateInBatches(values, batchSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save : !!! underlying implementation is different with GORM
|
||||||
|
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
|
||||||
|
func (s scaStorageFaceDo) Save(values ...*model.ScaStorageFace) error {
|
||||||
|
if len(values) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return s.DO.Save(values)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) First() (*model.ScaStorageFace, error) {
|
||||||
|
if result, err := s.DO.First(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return result.(*model.ScaStorageFace), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Take() (*model.ScaStorageFace, error) {
|
||||||
|
if result, err := s.DO.Take(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return result.(*model.ScaStorageFace), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Last() (*model.ScaStorageFace, error) {
|
||||||
|
if result, err := s.DO.Last(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return result.(*model.ScaStorageFace), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Find() ([]*model.ScaStorageFace, error) {
|
||||||
|
result, err := s.DO.Find()
|
||||||
|
return result.([]*model.ScaStorageFace), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.ScaStorageFace, err error) {
|
||||||
|
buf := make([]*model.ScaStorageFace, 0, batchSize)
|
||||||
|
err = s.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
|
||||||
|
defer func() { results = append(results, buf...) }()
|
||||||
|
return fc(tx, batch)
|
||||||
|
})
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) FindInBatches(result *[]*model.ScaStorageFace, batchSize int, fc func(tx gen.Dao, batch int) error) error {
|
||||||
|
return s.DO.FindInBatches(result, batchSize, fc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Attrs(attrs ...field.AssignExpr) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Attrs(attrs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Assign(attrs ...field.AssignExpr) IScaStorageFaceDo {
|
||||||
|
return s.withDO(s.DO.Assign(attrs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Joins(fields ...field.RelationField) IScaStorageFaceDo {
|
||||||
|
for _, _f := range fields {
|
||||||
|
s = *s.withDO(s.DO.Joins(_f))
|
||||||
|
}
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Preload(fields ...field.RelationField) IScaStorageFaceDo {
|
||||||
|
for _, _f := range fields {
|
||||||
|
s = *s.withDO(s.DO.Preload(_f))
|
||||||
|
}
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) FirstOrInit() (*model.ScaStorageFace, error) {
|
||||||
|
if result, err := s.DO.FirstOrInit(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return result.(*model.ScaStorageFace), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) FirstOrCreate() (*model.ScaStorageFace, error) {
|
||||||
|
if result, err := s.DO.FirstOrCreate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return result.(*model.ScaStorageFace), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) FindByPage(offset int, limit int) (result []*model.ScaStorageFace, count int64, err error) {
|
||||||
|
result, err = s.Offset(offset).Limit(limit).Find()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if size := len(result); 0 < limit && 0 < size && size < limit {
|
||||||
|
count = int64(size + offset)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err = s.Offset(-1).Limit(-1).Count()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
|
||||||
|
count, err = s.Count()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.Offset(offset).Limit(limit).Scan(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Scan(result interface{}) (err error) {
|
||||||
|
return s.DO.Scan(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scaStorageFaceDo) Delete(models ...*model.ScaStorageFace) (result gen.ResultInfo, err error) {
|
||||||
|
return s.DO.Delete(models)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *scaStorageFaceDo) withDO(do gen.Dao) *scaStorageFaceDo {
|
||||||
|
s.DO = *do.(*gen.DO)
|
||||||
|
return s
|
||||||
|
}
|
Binary file not shown.
BIN
app/aisvc/resources/models/face/mmod_human_face_detector.dat
Normal file
BIN
app/aisvc/resources/models/face/mmod_human_face_detector.dat
Normal file
Binary file not shown.
Binary file not shown.
39
app/aisvc/rpc/aisvc.go
Normal file
39
app/aisvc/rpc/aisvc.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"schisandra-album-cloud-microservices/app/aisvc/rpc/internal/config"
|
||||||
|
aiserviceServer "schisandra-album-cloud-microservices/app/aisvc/rpc/internal/server/aiservice"
|
||||||
|
"schisandra-album-cloud-microservices/app/aisvc/rpc/internal/svc"
|
||||||
|
"schisandra-album-cloud-microservices/app/aisvc/rpc/pb"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/conf"
|
||||||
|
"github.com/zeromicro/go-zero/core/service"
|
||||||
|
"github.com/zeromicro/go-zero/zrpc"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/reflection"
|
||||||
|
)
|
||||||
|
|
||||||
|
var configFile = flag.String("f", "rpc/etc/aisvc.yaml", "the config file")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
var c config.Config
|
||||||
|
conf.MustLoad(*configFile, &c)
|
||||||
|
ctx := svc.NewServiceContext(c)
|
||||||
|
|
||||||
|
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
|
||||||
|
pb.RegisterAiServiceServer(grpcServer, aiserviceServer.NewAiServiceServer(ctx))
|
||||||
|
|
||||||
|
if c.Mode == service.DevMode || c.Mode == service.TestMode {
|
||||||
|
reflection.Register(grpcServer)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer s.Stop()
|
||||||
|
|
||||||
|
fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
|
||||||
|
s.Start()
|
||||||
|
}
|
18
app/aisvc/rpc/aisvc.proto
Normal file
18
app/aisvc/rpc/aisvc.proto
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package ai;
|
||||||
|
option go_package = "./pb";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
message FaceRecognitionRequest {
|
||||||
|
bytes face = 1;
|
||||||
|
string user_id = 2;
|
||||||
|
}
|
||||||
|
message FaceRecognitionResponse {
|
||||||
|
int64 face_id = 1;
|
||||||
|
}
|
||||||
|
service AiService {
|
||||||
|
// FaceRecognition
|
||||||
|
rpc FaceRecognition (FaceRecognitionRequest) returns (FaceRecognitionResponse);
|
||||||
|
}
|
40
app/aisvc/rpc/client/aiservice/ai_service.go
Normal file
40
app/aisvc/rpc/client/aiservice/ai_service.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl 1.7.3
|
||||||
|
// Source: aisvc.proto
|
||||||
|
|
||||||
|
package aiservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"schisandra-album-cloud-microservices/app/aisvc/rpc/pb"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/zrpc"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
FaceRecognitionRequest = pb.FaceRecognitionRequest
|
||||||
|
FaceRecognitionResponse = pb.FaceRecognitionResponse
|
||||||
|
|
||||||
|
AiService interface {
|
||||||
|
// FaceRecognition
|
||||||
|
FaceRecognition(ctx context.Context, in *FaceRecognitionRequest, opts ...grpc.CallOption) (*FaceRecognitionResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultAiService struct {
|
||||||
|
cli zrpc.Client
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewAiService(cli zrpc.Client) AiService {
|
||||||
|
return &defaultAiService{
|
||||||
|
cli: cli,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FaceRecognition
|
||||||
|
func (m *defaultAiService) FaceRecognition(ctx context.Context, in *FaceRecognitionRequest, opts ...grpc.CallOption) (*FaceRecognitionResponse, error) {
|
||||||
|
client := pb.NewAiServiceClient(m.cli.Conn())
|
||||||
|
return client.FaceRecognition(ctx, in, opts...)
|
||||||
|
}
|
22
app/aisvc/rpc/etc/aisvc.yaml
Normal file
22
app/aisvc/rpc/etc/aisvc.yaml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Name: aisvc.rpc
|
||||||
|
ListenOn: 0.0.0.0:8080
|
||||||
|
Etcd:
|
||||||
|
Hosts:
|
||||||
|
- 127.0.0.1:2379
|
||||||
|
Key: aisvc.rpc
|
||||||
|
# MySQL 配置
|
||||||
|
Mysql:
|
||||||
|
# 数据源dsn
|
||||||
|
DataSource: root:LDQ20020618xxx@tcp(1.95.0.111:3306)/schisandra-cloud-album?charset=utf8mb4&parseTime=True&loc=Local
|
||||||
|
# 最大连接数
|
||||||
|
MaxOpenConn: 10
|
||||||
|
# 最大空闲连接数
|
||||||
|
MaxIdleConn: 5
|
||||||
|
# RedisConf 配置
|
||||||
|
RedisConf:
|
||||||
|
# Redis 地址
|
||||||
|
Host: 1.95.0.111:6379
|
||||||
|
# Redis 密码
|
||||||
|
Pass: LDQ20020618xxx
|
||||||
|
# Redis 数据库
|
||||||
|
DB: 0
|
3
app/aisvc/rpc/generate.go
Normal file
3
app/aisvc/rpc/generate.go
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
//go:generate goctl rpc protoc aisvc.proto --go_out=. --go-grpc_out=. --zrpc_out=. --client=true -m --style=go_zero
|
18
app/aisvc/rpc/internal/config/config.go
Normal file
18
app/aisvc/rpc/internal/config/config.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import "github.com/zeromicro/go-zero/zrpc"
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
zrpc.RpcServerConf
|
||||||
|
|
||||||
|
Mysql struct {
|
||||||
|
DataSource string
|
||||||
|
MaxOpenConn int
|
||||||
|
MaxIdleConn int
|
||||||
|
}
|
||||||
|
RedisConf struct {
|
||||||
|
Host string
|
||||||
|
Pass string
|
||||||
|
DB int
|
||||||
|
}
|
||||||
|
}
|
355
app/aisvc/rpc/internal/logic/aiservice/face_recognition_logic.go
Normal file
355
app/aisvc/rpc/internal/logic/aiservice/face_recognition_logic.go
Normal file
@@ -0,0 +1,355 @@
|
|||||||
|
package aiservicelogic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Kagami/go-face"
|
||||||
|
"github.com/ccpwcn/kgo"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"image"
|
||||||
|
"image/jpeg"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"schisandra-album-cloud-microservices/app/aisvc/model/mysql/model"
|
||||||
|
"schisandra-album-cloud-microservices/app/aisvc/rpc/internal/svc"
|
||||||
|
"schisandra-album-cloud-microservices/app/aisvc/rpc/pb"
|
||||||
|
"schisandra-album-cloud-microservices/common/constant"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FaceRecognitionLogic struct {
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
logx.Logger
|
||||||
|
directoryCache sync.Map
|
||||||
|
wg sync.WaitGroup
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFaceRecognitionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FaceRecognitionLogic {
|
||||||
|
return &FaceRecognitionLogic{
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
directoryCache: sync.Map{},
|
||||||
|
wg: sync.WaitGroup{},
|
||||||
|
mu: sync.Mutex{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FaceRecognition 人脸识别
|
||||||
|
func (l *FaceRecognitionLogic) FaceRecognition(in *pb.FaceRecognitionRequest) (*pb.FaceRecognitionResponse, error) {
|
||||||
|
// 提取人脸特征
|
||||||
|
faceFeatures, err := l.svcCtx.FaceRecognizer.RecognizeSingle(in.GetFace())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if faceFeatures == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hashKey := fmt.Sprintf("user:%s:faces", in.GetUserId())
|
||||||
|
// 从 Redis 加载人脸数据
|
||||||
|
samples, ids, err := l.loadFacesFromRedisHash(hashKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to query Redis: %v", err)
|
||||||
|
}
|
||||||
|
// 如果缓存中没有数据,则查询数据库
|
||||||
|
if len(samples) == 0 {
|
||||||
|
samples, ids, err = l.loadExistingFaces(in.GetUserId())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果数据库也没有数据,直接保存当前人脸
|
||||||
|
if len(samples) == 0 || len(ids) == 0 {
|
||||||
|
return l.saveNewFace(in, faceFeatures, hashKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将数据写入 Redis
|
||||||
|
err = l.cacheFacesToRedisHash(hashKey, samples, ids)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to cache faces to Redis: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置人脸特征
|
||||||
|
l.svcCtx.FaceRecognizer.SetSamples(samples, ids)
|
||||||
|
|
||||||
|
// 人脸分类
|
||||||
|
classify := l.svcCtx.FaceRecognizer.ClassifyThreshold(faceFeatures.Descriptor, 0.6)
|
||||||
|
if classify >= 0 {
|
||||||
|
return &pb.FaceRecognitionResponse{
|
||||||
|
FaceId: int64(ids[classify]),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果未找到匹配的人脸,则保存为新样本
|
||||||
|
return l.saveNewFace(in, faceFeatures, hashKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存新的人脸样本到数据库和 Redis
|
||||||
|
func (l *FaceRecognitionLogic) saveNewFace(in *pb.FaceRecognitionRequest, faceFeatures *face.Face, hashKey string) (*pb.FaceRecognitionResponse, error) {
|
||||||
|
// 人脸有效性判断 (大小必须大于50)
|
||||||
|
if !l.isFaceValid(faceFeatures.Rectangle) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存人脸图片到本地
|
||||||
|
faceImagePath, err := l.saveCroppedFaceToLocal(in.GetFace(), faceFeatures.Rectangle, "face_samples", in.GetUserId())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存到数据库
|
||||||
|
storageFace, err := l.saveFaceToDatabase(in.GetUserId(), faceFeatures.Descriptor, faceImagePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将新增数据写入 Redis
|
||||||
|
err = l.appendFaceToRedisHash(hashKey, storageFace.ID, faceFeatures.Descriptor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to append face to Redis: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pb.FaceRecognitionResponse{
|
||||||
|
FaceId: storageFace.ID,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载数据库中的已有人脸
|
||||||
|
func (l *FaceRecognitionLogic) loadExistingFaces(userId string) ([]face.Descriptor, []int32, error) {
|
||||||
|
if userId == "" {
|
||||||
|
return nil, nil, fmt.Errorf("user ID is required")
|
||||||
|
}
|
||||||
|
storageFace := l.svcCtx.DB.ScaStorageFace
|
||||||
|
existingFaces, err := storageFace.
|
||||||
|
Select(storageFace.FaceVector, storageFace.ID).
|
||||||
|
Where(storageFace.UserID.Eq(userId), storageFace.FaceType.Eq(constant.FaceTypeSample)).
|
||||||
|
Find()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if len(existingFaces) == 0 {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var samples []face.Descriptor
|
||||||
|
var ids []int32
|
||||||
|
// 使用并发处理每个数据
|
||||||
|
for _, existingFace := range existingFaces {
|
||||||
|
l.wg.Add(1)
|
||||||
|
go func(faceData *model.ScaStorageFace) {
|
||||||
|
defer l.wg.Done()
|
||||||
|
|
||||||
|
var descriptor face.Descriptor
|
||||||
|
if err = json.Unmarshal([]byte(faceData.FaceVector), &descriptor); err != nil {
|
||||||
|
l.Errorf("failed to unmarshal face vector: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 使用锁来保证并发访问时对切片的安全操作
|
||||||
|
l.mu.Lock()
|
||||||
|
samples = append(samples, descriptor)
|
||||||
|
ids = append(ids, int32(faceData.ID))
|
||||||
|
l.mu.Unlock()
|
||||||
|
}(existingFace)
|
||||||
|
}
|
||||||
|
l.wg.Wait()
|
||||||
|
return samples, ids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
minFaceWidth = 50 // 最小允许的人脸宽度
|
||||||
|
minFaceHeight = 50 // 最小允许的人脸高度
|
||||||
|
)
|
||||||
|
|
||||||
|
// 判断人脸是否有效
|
||||||
|
func (l *FaceRecognitionLogic) isFaceValid(rect image.Rectangle) bool {
|
||||||
|
width := rect.Dx()
|
||||||
|
height := rect.Dy()
|
||||||
|
return width >= minFaceWidth && height >= minFaceHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存人脸特征和路径到数据库
|
||||||
|
func (l *FaceRecognitionLogic) saveFaceToDatabase(userId string, descriptor face.Descriptor, faceImagePath string) (*model.ScaStorageFace, error) {
|
||||||
|
jsonBytes, err := json.Marshal(descriptor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
storageFace := model.ScaStorageFace{
|
||||||
|
FaceVector: string(jsonBytes),
|
||||||
|
FaceImagePath: faceImagePath,
|
||||||
|
FaceType: constant.FaceTypeSample,
|
||||||
|
UserID: userId,
|
||||||
|
}
|
||||||
|
err = l.svcCtx.DB.ScaStorageFace.Create(&storageFace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &storageFace, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *FaceRecognitionLogic) saveCroppedFaceToLocal(faceImage []byte, rect image.Rectangle, baseSavePath string, userID string) (string, error) {
|
||||||
|
// 动态生成用户目录和时间分级目录
|
||||||
|
subDir := filepath.Join(baseSavePath, userID, time.Now().Format("2006/01")) // 格式:<baseSavePath>/<userID>/YYYY/MM
|
||||||
|
|
||||||
|
// 缓存目录检查,避免重复调用 os.MkdirAll
|
||||||
|
if !l.isDirectoryCached(subDir) {
|
||||||
|
if err := os.MkdirAll(subDir, os.ModePerm); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create directory: %w", err)
|
||||||
|
}
|
||||||
|
l.cacheDirectory(subDir) // 缓存已创建的目录路径
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解码图像
|
||||||
|
img, _, err := image.Decode(bytes.NewReader(faceImage))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("image decode failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取图像边界
|
||||||
|
imgBounds := img.Bounds()
|
||||||
|
// 增加边距(比如 20 像素)
|
||||||
|
margin := 20
|
||||||
|
extendedRect := image.Rect(
|
||||||
|
max(rect.Min.X-margin, imgBounds.Min.X), // 确保不超出左边界
|
||||||
|
max(rect.Min.Y-margin, imgBounds.Min.Y), // 确保不超出上边界
|
||||||
|
min(rect.Max.X+margin, imgBounds.Max.X), // 确保不超出右边界
|
||||||
|
min(rect.Max.Y+margin, imgBounds.Max.Y), // 确保不超出下边界
|
||||||
|
)
|
||||||
|
// 裁剪图像
|
||||||
|
croppedImage := img.(interface {
|
||||||
|
SubImage(r image.Rectangle) image.Image
|
||||||
|
}).SubImage(extendedRect)
|
||||||
|
|
||||||
|
// 生成唯一文件名(时间戳 + UUID)
|
||||||
|
fileName := fmt.Sprintf("%s_%s.jpg", time.Now().Format("20060102_150405"), kgo.SimpleUuid())
|
||||||
|
outputPath := filepath.Join(subDir, fileName)
|
||||||
|
|
||||||
|
// 写入文件
|
||||||
|
if err = l.writeImageToFile(outputPath, croppedImage); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return outputPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断目录是否已缓存
|
||||||
|
func (l *FaceRecognitionLogic) isDirectoryCached(dir string) bool {
|
||||||
|
_, exists := l.directoryCache.Load(dir)
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缓存目录
|
||||||
|
func (l *FaceRecognitionLogic) cacheDirectory(dir string) {
|
||||||
|
l.directoryCache.Store(dir, struct{}{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将图像写入文件
|
||||||
|
func (l *FaceRecognitionLogic) writeImageToFile(path string, img image.Image) error {
|
||||||
|
file, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create file: %w", err)
|
||||||
|
}
|
||||||
|
defer func(file *os.File) {
|
||||||
|
_ = file.Close()
|
||||||
|
}(file)
|
||||||
|
|
||||||
|
if err = jpeg.Encode(file, img, nil); err != nil {
|
||||||
|
return fmt.Errorf("failed to encode and save image: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从 Redis 的 Hash 中加载人脸数据
|
||||||
|
func (l *FaceRecognitionLogic) loadFacesFromRedisHash(hashKey string) ([]face.Descriptor, []int32, error) {
|
||||||
|
// 从 Redis 获取 Hash 的所有字段和值
|
||||||
|
data, err := l.svcCtx.RedisClient.HGetAll(l.ctx, hashKey).Result()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var samples []face.Descriptor
|
||||||
|
var ids []int32
|
||||||
|
for idStr, descriptorStr := range data {
|
||||||
|
var descriptor face.Descriptor
|
||||||
|
if err = json.Unmarshal([]byte(descriptorStr), &descriptor); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换 ID 为 int32
|
||||||
|
id, err := parseInt32(idStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
samples = append(samples, descriptor)
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
return samples, ids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将人脸数据写入 Redis 的 Hash
|
||||||
|
func (l *FaceRecognitionLogic) cacheFacesToRedisHash(hashKey string, samples []face.Descriptor, ids []int32) error {
|
||||||
|
// 开启事务
|
||||||
|
pipe := l.svcCtx.RedisClient.Pipeline()
|
||||||
|
|
||||||
|
for i := range samples {
|
||||||
|
descriptorData, err := json.Marshal(samples[i])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 HSET 设置 Hash 字段和值
|
||||||
|
pipe.HSet(l.ctx, hashKey, fmt.Sprintf("%d", ids[i]), descriptorData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置缓存过期时间
|
||||||
|
pipe.Expire(l.ctx, hashKey, 3600*time.Second)
|
||||||
|
|
||||||
|
_, err := pipe.Exec(l.ctx)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将新增的人脸数据追加到 Redis 的 Hash
|
||||||
|
func (l *FaceRecognitionLogic) appendFaceToRedisHash(hashKey string, id int64, descriptor face.Descriptor) error {
|
||||||
|
descriptorData, err := json.Marshal(descriptor)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 追加数据到 Hash
|
||||||
|
err = l.svcCtx.RedisClient.HSet(l.ctx, hashKey, fmt.Sprintf("%d", id), descriptorData).Err()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 检查是否已设置过期时间
|
||||||
|
ttl, err := l.svcCtx.RedisClient.TTL(l.ctx, hashKey).Result()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果未设置过期时间或已经过期,设置固定过期时间
|
||||||
|
if ttl < 0 {
|
||||||
|
err = l.svcCtx.RedisClient.Expire(l.ctx, hashKey, 3600*time.Second).Err()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数:字符串转换为 int32
|
||||||
|
func parseInt32(s string) (int32, error) {
|
||||||
|
var i int64
|
||||||
|
var err error
|
||||||
|
if i, err = strconv.ParseInt(s, 10, 32); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return int32(i), nil
|
||||||
|
}
|
30
app/aisvc/rpc/internal/server/aiservice/ai_service_server.go
Normal file
30
app/aisvc/rpc/internal/server/aiservice/ai_service_server.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl 1.7.3
|
||||||
|
// Source: aisvc.proto
|
||||||
|
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"schisandra-album-cloud-microservices/app/aisvc/rpc/internal/logic/aiservice"
|
||||||
|
"schisandra-album-cloud-microservices/app/aisvc/rpc/internal/svc"
|
||||||
|
"schisandra-album-cloud-microservices/app/aisvc/rpc/pb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AiServiceServer struct {
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
pb.UnimplementedAiServiceServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAiServiceServer(svcCtx *svc.ServiceContext) *AiServiceServer {
|
||||||
|
return &AiServiceServer{
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FaceRecognition
|
||||||
|
func (s *AiServiceServer) FaceRecognition(ctx context.Context, in *pb.FaceRecognitionRequest) (*pb.FaceRecognitionResponse, error) {
|
||||||
|
l := aiservicelogic.NewFaceRecognitionLogic(ctx, s.svcCtx)
|
||||||
|
return l.FaceRecognition(in)
|
||||||
|
}
|
29
app/aisvc/rpc/internal/svc/service_context.go
Normal file
29
app/aisvc/rpc/internal/svc/service_context.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package svc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Kagami/go-face"
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
|
"schisandra-album-cloud-microservices/app/aisvc/model/mysql"
|
||||||
|
"schisandra-album-cloud-microservices/app/aisvc/model/mysql/query"
|
||||||
|
"schisandra-album-cloud-microservices/app/aisvc/rpc/internal/config"
|
||||||
|
"schisandra-album-cloud-microservices/common/face_recognizer"
|
||||||
|
"schisandra-album-cloud-microservices/common/redisx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServiceContext struct {
|
||||||
|
Config config.Config
|
||||||
|
FaceRecognizer *face.Recognizer
|
||||||
|
DB *query.Query
|
||||||
|
RedisClient *redis.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServiceContext(c config.Config) *ServiceContext {
|
||||||
|
redisClient := redisx.NewRedis(c.RedisConf.Host, c.RedisConf.Pass, c.RedisConf.DB)
|
||||||
|
_, queryDB := mysql.NewMySQL(c.Mysql.DataSource, c.Mysql.MaxOpenConn, c.Mysql.MaxIdleConn, redisClient)
|
||||||
|
return &ServiceContext{
|
||||||
|
Config: c,
|
||||||
|
FaceRecognizer: face_recognizer.NewFaceRecognition(),
|
||||||
|
DB: queryDB,
|
||||||
|
RedisClient: redisClient,
|
||||||
|
}
|
||||||
|
}
|
192
app/aisvc/rpc/pb/aisvc.pb.go
Normal file
192
app/aisvc/rpc/pb/aisvc.pb.go
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.35.1
|
||||||
|
// protoc v3.19.4
|
||||||
|
// source: aisvc.proto
|
||||||
|
|
||||||
|
package pb
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type FaceRecognitionRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Face []byte `protobuf:"bytes,1,opt,name=face,proto3" json:"face,omitempty"`
|
||||||
|
UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FaceRecognitionRequest) Reset() {
|
||||||
|
*x = FaceRecognitionRequest{}
|
||||||
|
mi := &file_aisvc_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FaceRecognitionRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*FaceRecognitionRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *FaceRecognitionRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_aisvc_proto_msgTypes[0]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use FaceRecognitionRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*FaceRecognitionRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_aisvc_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FaceRecognitionRequest) GetFace() []byte {
|
||||||
|
if x != nil {
|
||||||
|
return x.Face
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FaceRecognitionRequest) GetUserId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.UserId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type FaceRecognitionResponse struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
FaceId int64 `protobuf:"varint,1,opt,name=face_id,json=faceId,proto3" json:"face_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FaceRecognitionResponse) Reset() {
|
||||||
|
*x = FaceRecognitionResponse{}
|
||||||
|
mi := &file_aisvc_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FaceRecognitionResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*FaceRecognitionResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *FaceRecognitionResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_aisvc_proto_msgTypes[1]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use FaceRecognitionResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*FaceRecognitionResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_aisvc_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FaceRecognitionResponse) GetFaceId() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.FaceId
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_aisvc_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_aisvc_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x0b, 0x61, 0x69, 0x73, 0x76, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x61,
|
||||||
|
0x69, 0x22, 0x45, 0x0a, 0x16, 0x46, 0x61, 0x63, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x67, 0x6e, 0x69,
|
||||||
|
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x66,
|
||||||
|
0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x61, 0x63, 0x65, 0x12,
|
||||||
|
0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||||
|
0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x32, 0x0a, 0x17, 0x46, 0x61, 0x63, 0x65,
|
||||||
|
0x52, 0x65, 0x63, 0x6f, 0x67, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||||
|
0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01,
|
||||||
|
0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x66, 0x61, 0x63, 0x65, 0x49, 0x64, 0x32, 0x57, 0x0a, 0x09,
|
||||||
|
0x41, 0x69, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x0f, 0x46, 0x61, 0x63,
|
||||||
|
0x65, 0x52, 0x65, 0x63, 0x6f, 0x67, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x2e, 0x61,
|
||||||
|
0x69, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x67, 0x6e, 0x69, 0x74, 0x69, 0x6f,
|
||||||
|
0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x61, 0x69, 0x2e, 0x46, 0x61,
|
||||||
|
0x63, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x67, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73,
|
||||||
|
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x06, 0x5a, 0x04, 0x2e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70,
|
||||||
|
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_aisvc_proto_rawDescOnce sync.Once
|
||||||
|
file_aisvc_proto_rawDescData = file_aisvc_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_aisvc_proto_rawDescGZIP() []byte {
|
||||||
|
file_aisvc_proto_rawDescOnce.Do(func() {
|
||||||
|
file_aisvc_proto_rawDescData = protoimpl.X.CompressGZIP(file_aisvc_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_aisvc_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_aisvc_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||||
|
var file_aisvc_proto_goTypes = []any{
|
||||||
|
(*FaceRecognitionRequest)(nil), // 0: ai.FaceRecognitionRequest
|
||||||
|
(*FaceRecognitionResponse)(nil), // 1: ai.FaceRecognitionResponse
|
||||||
|
}
|
||||||
|
var file_aisvc_proto_depIdxs = []int32{
|
||||||
|
0, // 0: ai.AiService.FaceRecognition:input_type -> ai.FaceRecognitionRequest
|
||||||
|
1, // 1: ai.AiService.FaceRecognition:output_type -> ai.FaceRecognitionResponse
|
||||||
|
1, // [1:2] is the sub-list for method output_type
|
||||||
|
0, // [0:1] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_aisvc_proto_init() }
|
||||||
|
func file_aisvc_proto_init() {
|
||||||
|
if File_aisvc_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_aisvc_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 2,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 1,
|
||||||
|
},
|
||||||
|
GoTypes: file_aisvc_proto_goTypes,
|
||||||
|
DependencyIndexes: file_aisvc_proto_depIdxs,
|
||||||
|
MessageInfos: file_aisvc_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_aisvc_proto = out.File
|
||||||
|
file_aisvc_proto_rawDesc = nil
|
||||||
|
file_aisvc_proto_goTypes = nil
|
||||||
|
file_aisvc_proto_depIdxs = nil
|
||||||
|
}
|
123
app/aisvc/rpc/pb/aisvc_grpc.pb.go
Normal file
123
app/aisvc/rpc/pb/aisvc_grpc.pb.go
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// - protoc-gen-go-grpc v1.5.1
|
||||||
|
// - protoc v3.19.4
|
||||||
|
// source: aisvc.proto
|
||||||
|
|
||||||
|
package pb
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
// Requires gRPC-Go v1.64.0 or later.
|
||||||
|
const _ = grpc.SupportPackageIsVersion9
|
||||||
|
|
||||||
|
const (
|
||||||
|
AiService_FaceRecognition_FullMethodName = "/ai.AiService/FaceRecognition"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AiServiceClient is the client API for AiService service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
type AiServiceClient interface {
|
||||||
|
// FaceRecognition
|
||||||
|
FaceRecognition(ctx context.Context, in *FaceRecognitionRequest, opts ...grpc.CallOption) (*FaceRecognitionResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type aiServiceClient struct {
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAiServiceClient(cc grpc.ClientConnInterface) AiServiceClient {
|
||||||
|
return &aiServiceClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aiServiceClient) FaceRecognition(ctx context.Context, in *FaceRecognitionRequest, opts ...grpc.CallOption) (*FaceRecognitionResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(FaceRecognitionResponse)
|
||||||
|
err := c.cc.Invoke(ctx, AiService_FaceRecognition_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AiServiceServer is the server API for AiService service.
|
||||||
|
// All implementations must embed UnimplementedAiServiceServer
|
||||||
|
// for forward compatibility.
|
||||||
|
type AiServiceServer interface {
|
||||||
|
// FaceRecognition
|
||||||
|
FaceRecognition(context.Context, *FaceRecognitionRequest) (*FaceRecognitionResponse, error)
|
||||||
|
mustEmbedUnimplementedAiServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedAiServiceServer must be embedded to have
|
||||||
|
// forward compatible implementations.
|
||||||
|
//
|
||||||
|
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||||
|
// pointer dereference when methods are called.
|
||||||
|
type UnimplementedAiServiceServer struct{}
|
||||||
|
|
||||||
|
func (UnimplementedAiServiceServer) FaceRecognition(context.Context, *FaceRecognitionRequest) (*FaceRecognitionResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method FaceRecognition not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedAiServiceServer) mustEmbedUnimplementedAiServiceServer() {}
|
||||||
|
func (UnimplementedAiServiceServer) testEmbeddedByValue() {}
|
||||||
|
|
||||||
|
// UnsafeAiServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
// Use of this interface is not recommended, as added methods to AiServiceServer will
|
||||||
|
// result in compilation errors.
|
||||||
|
type UnsafeAiServiceServer interface {
|
||||||
|
mustEmbedUnimplementedAiServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterAiServiceServer(s grpc.ServiceRegistrar, srv AiServiceServer) {
|
||||||
|
// If the following call pancis, it indicates UnimplementedAiServiceServer was
|
||||||
|
// embedded by pointer and is nil. This will cause panics if an
|
||||||
|
// unimplemented method is ever invoked, so we test this at initialization
|
||||||
|
// time to prevent it from happening at runtime later due to I/O.
|
||||||
|
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||||
|
t.testEmbeddedByValue()
|
||||||
|
}
|
||||||
|
s.RegisterService(&AiService_ServiceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _AiService_FaceRecognition_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(FaceRecognitionRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(AiServiceServer).FaceRecognition(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: AiService_FaceRecognition_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(AiServiceServer).FaceRecognition(ctx, req.(*FaceRecognitionRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AiService_ServiceDesc is the grpc.ServiceDesc for AiService service.
|
||||||
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
var AiService_ServiceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "ai.AiService",
|
||||||
|
HandlerType: (*AiServiceServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "FaceRecognition",
|
||||||
|
Handler: _AiService_FaceRecognition_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{},
|
||||||
|
Metadata: "aisvc.proto",
|
||||||
|
}
|
@@ -18,6 +18,12 @@ CpuThreshold: 900
|
|||||||
MaxBytes: 10485760
|
MaxBytes: 10485760
|
||||||
# 是否打印详细日志
|
# 是否打印详细日志
|
||||||
Verbose: false
|
Verbose: false
|
||||||
|
# RPC 配置
|
||||||
|
AiSvcRpc:
|
||||||
|
Etcd:
|
||||||
|
Hosts:
|
||||||
|
- 127.0.0.1:2379
|
||||||
|
Key: aisvc.rpc
|
||||||
# 日志配置
|
# 日志配置
|
||||||
Log:
|
Log:
|
||||||
# 服务名称
|
# 服务名称
|
||||||
@@ -162,3 +168,6 @@ SMS:
|
|||||||
Username: landaiqing
|
Username: landaiqing
|
||||||
# 短信宝用户密码
|
# 短信宝用户密码
|
||||||
Password: $LDQ20020618xxx$
|
Password: $LDQ20020618xxx$
|
||||||
|
Map:
|
||||||
|
# 高德地图API Key
|
||||||
|
Key: 54823a494909959a9c8cd8af101bbc32
|
@@ -1,10 +1,14 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import "github.com/zeromicro/go-zero/rest"
|
import (
|
||||||
|
"github.com/zeromicro/go-zero/rest"
|
||||||
|
"github.com/zeromicro/go-zero/zrpc"
|
||||||
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
rest.RestConf
|
rest.RestConf
|
||||||
Web struct {
|
AiSvcRpc zrpc.RpcClientConf
|
||||||
|
Web struct {
|
||||||
URL string
|
URL string
|
||||||
}
|
}
|
||||||
Auth struct {
|
Auth struct {
|
||||||
@@ -67,4 +71,7 @@ type Config struct {
|
|||||||
Password string
|
Password string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Map struct {
|
||||||
|
Key string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,12 +2,23 @@ package storage
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"io"
|
||||||
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"schisandra-album-cloud-microservices/app/aisvc/rpc/pb"
|
||||||
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
|
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
|
||||||
|
"schisandra-album-cloud-microservices/app/auth/api/internal/types"
|
||||||
|
"schisandra-album-cloud-microservices/app/auth/model/mysql/model"
|
||||||
"schisandra-album-cloud-microservices/common/encrypt"
|
"schisandra-album-cloud-microservices/common/encrypt"
|
||||||
|
"schisandra-album-cloud-microservices/common/gao_map"
|
||||||
"schisandra-album-cloud-microservices/common/storage/config"
|
"schisandra-album-cloud-microservices/common/storage/config"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UploadFileLogic struct {
|
type UploadFileLogic struct {
|
||||||
@@ -25,36 +36,199 @@ func NewUploadFileLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Upload
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *UploadFileLogic) UploadFile(r *http.Request) (resp string, err error) {
|
func (l *UploadFileLogic) UploadFile(r *http.Request) (resp string, err error) {
|
||||||
|
// 获取用户 ID
|
||||||
|
uid, err := l.getUserID()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析上传的文件
|
||||||
|
file, header, err := l.getUploadedFile(r)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer func(file multipart.File) {
|
||||||
|
_ = file.Close()
|
||||||
|
}(file)
|
||||||
|
|
||||||
|
// 解析 AI 识别结果
|
||||||
|
result, err := l.parseAIRecognitionResult(r)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var faceId int64
|
||||||
|
bytes, err := io.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// 人脸识别
|
||||||
|
face, err := l.svcCtx.AiSvcRpc.FaceRecognition(l.ctx, &pb.FaceRecognitionRequest{Face: bytes, UserId: uid})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if face != nil {
|
||||||
|
faceId = face.GetFaceId()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 EXIF 信息
|
||||||
|
exif, err := l.parseExifData(result.Exif)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取拍摄时间
|
||||||
|
originalDateTime, err := l.extractOriginalDateTime(exif)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取 GPS 信息
|
||||||
|
latitude, longitude := l.extractGPSCoordinates(exif)
|
||||||
|
|
||||||
|
// 根据 GPS 信息获取地理位置信息
|
||||||
|
locationString, gpsString, err := l.getGeoLocation(latitude, longitude)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传文件到 OSS
|
||||||
|
if err = l.uploadFileToOSS(uid, header, file); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将 EXIF 和文件信息存入数据库
|
||||||
|
if err = l.saveFileInfoToDB(uid, header, result, originalDateTime, gpsString, locationString, exif, faceId); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return "success", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户 ID
|
||||||
|
func (l *UploadFileLogic) getUserID() (string, error) {
|
||||||
uid, ok := l.ctx.Value("user_id").(string)
|
uid, ok := l.ctx.Value("user_id").(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", errors.New("user_id not found")
|
return "", errors.New("user_id not found")
|
||||||
}
|
}
|
||||||
|
return uid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析上传的文件
|
||||||
|
func (l *UploadFileLogic) getUploadedFile(r *http.Request) (multipart.File, *multipart.FileHeader, error) {
|
||||||
file, header, err := r.FormFile("file")
|
file, header, err := r.FormFile("file")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.New("file not found")
|
return nil, nil, errors.New("file not found")
|
||||||
}
|
}
|
||||||
defer file.Close()
|
return file, header, nil
|
||||||
//formValue := r.PostFormValue("result")
|
}
|
||||||
//
|
|
||||||
//var result types.File
|
// 解析 AI 识别结果
|
||||||
//err = json.Unmarshal([]byte(formValue), &result)
|
func (l *UploadFileLogic) parseAIRecognitionResult(r *http.Request) (types.File, error) {
|
||||||
//if err != nil {
|
formValue := r.PostFormValue("data")
|
||||||
// return "", errors.New("invalid result")
|
var result types.File
|
||||||
//}
|
if err := json.Unmarshal([]byte(formValue), &result); err != nil {
|
||||||
//fmt.Println(result)
|
return result, errors.New("invalid result")
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 EXIF 数据
|
||||||
|
func (l *UploadFileLogic) parseExifData(exifData interface{}) (map[string]interface{}, error) {
|
||||||
|
marshaledExif, err := json.Marshal(exifData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("invalid exif")
|
||||||
|
}
|
||||||
|
|
||||||
|
var exif map[string]interface{}
|
||||||
|
if err = json.Unmarshal(marshaledExif, &exif); err != nil {
|
||||||
|
return nil, errors.New("invalid exif")
|
||||||
|
}
|
||||||
|
return exif, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取拍摄时间
|
||||||
|
func (l *UploadFileLogic) extractOriginalDateTime(exif map[string]interface{}) (string, error) {
|
||||||
|
if dateTimeOriginal, ok := exif["DateTimeOriginal"].(string); ok {
|
||||||
|
parsedTime, err := time.Parse(time.RFC3339, dateTimeOriginal)
|
||||||
|
if err == nil {
|
||||||
|
return parsedTime.Format("2006-01-02 15:04:05"), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取 GPS 信息
|
||||||
|
func (l *UploadFileLogic) extractGPSCoordinates(exif map[string]interface{}) (float64, float64) {
|
||||||
|
var latitude, longitude float64
|
||||||
|
if lat, ok := exif["latitude"].(float64); ok {
|
||||||
|
latitude = lat
|
||||||
|
}
|
||||||
|
if long, ok := exif["longitude"].(float64); ok {
|
||||||
|
longitude = long
|
||||||
|
}
|
||||||
|
return latitude, longitude
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据 GPS 信息获取地理位置信息
|
||||||
|
func (l *UploadFileLogic) getGeoLocation(latitude, longitude float64) (string, string, error) {
|
||||||
|
if latitude == 0 || longitude == 0 {
|
||||||
|
return "", "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
gpsString := fmt.Sprintf("[%f,%f]", latitude, longitude)
|
||||||
|
request := gao_map.ReGeoRequest{Location: fmt.Sprintf("%f,%f", latitude, longitude)}
|
||||||
|
|
||||||
|
location, err := l.svcCtx.GaoMap.Location.ReGeo(&request)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", errors.New("regeo failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
addressInfo := map[string]string{}
|
||||||
|
if location.ReGeoCode.AddressComponent.Country != "" {
|
||||||
|
addressInfo["county"] = location.ReGeoCode.AddressComponent.Country
|
||||||
|
}
|
||||||
|
if location.ReGeoCode.AddressComponent.Province != "" {
|
||||||
|
addressInfo["province"] = location.ReGeoCode.AddressComponent.Province
|
||||||
|
}
|
||||||
|
if location.ReGeoCode.AddressComponent.City != "" {
|
||||||
|
addressInfo["city"] = location.ReGeoCode.AddressComponent.City.(string)
|
||||||
|
}
|
||||||
|
if location.ReGeoCode.AddressComponent.District != "" {
|
||||||
|
addressInfo["district"] = location.ReGeoCode.AddressComponent.District.(string)
|
||||||
|
}
|
||||||
|
if location.ReGeoCode.AddressComponent.Township != "" {
|
||||||
|
addressInfo["township"] = location.ReGeoCode.AddressComponent.Township
|
||||||
|
}
|
||||||
|
|
||||||
|
locationString := ""
|
||||||
|
if len(addressInfo) > 0 {
|
||||||
|
addressJSON, err := json.Marshal(addressInfo)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", errors.New("marshal address info failed")
|
||||||
|
}
|
||||||
|
locationString = string(addressJSON)
|
||||||
|
}
|
||||||
|
|
||||||
|
return locationString, gpsString, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传文件到 OSS
|
||||||
|
func (l *UploadFileLogic) uploadFileToOSS(uid string, header *multipart.FileHeader, file multipart.File) error {
|
||||||
ossConfig := l.svcCtx.DB.ScaStorageConfig
|
ossConfig := l.svcCtx.DB.ScaStorageConfig
|
||||||
dbConfig, err := ossConfig.Where(ossConfig.UserID.Eq(uid)).First()
|
dbConfig, err := ossConfig.Where(ossConfig.UserID.Eq(uid)).First()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.New("oss config not found")
|
return errors.New("oss config not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
accessKey, err := encrypt.Decrypt(dbConfig.AccessKey, l.svcCtx.Config.Encrypt.Key)
|
accessKey, err := encrypt.Decrypt(dbConfig.AccessKey, l.svcCtx.Config.Encrypt.Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.New("decrypt access key failed")
|
return errors.New("decrypt access key failed")
|
||||||
}
|
}
|
||||||
secretKey, err := encrypt.Decrypt(dbConfig.SecretKey, l.svcCtx.Config.Encrypt.Key)
|
secretKey, err := encrypt.Decrypt(dbConfig.SecretKey, l.svcCtx.Config.Encrypt.Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.New("decrypt secret key failed")
|
return errors.New("decrypt secret key failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
storageConfig := &config.StorageConfig{
|
storageConfig := &config.StorageConfig{
|
||||||
Provider: dbConfig.Type,
|
Provider: dbConfig.Type,
|
||||||
Endpoint: dbConfig.Endpoint,
|
Endpoint: dbConfig.Endpoint,
|
||||||
@@ -63,13 +237,48 @@ func (l *UploadFileLogic) UploadFile(r *http.Request) (resp string, err error) {
|
|||||||
BucketName: dbConfig.Bucket,
|
BucketName: dbConfig.Bucket,
|
||||||
Region: dbConfig.Region,
|
Region: dbConfig.Region,
|
||||||
}
|
}
|
||||||
|
|
||||||
service, err := l.svcCtx.StorageManager.GetStorage(uid, storageConfig)
|
service, err := l.svcCtx.StorageManager.GetStorage(uid, storageConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.New("get storage failed")
|
return errors.New("get storage failed")
|
||||||
}
|
}
|
||||||
result, err := service.UploadFileSimple(l.ctx, dbConfig.Bucket, header.Filename, file, map[string]string{})
|
|
||||||
|
_, err = service.UploadFileSimple(l.ctx, dbConfig.Bucket, header.Filename, file, map[string]string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.New("upload file failed")
|
return errors.New("upload file failed")
|
||||||
}
|
}
|
||||||
return *result.ContentMD5, nil
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将 EXIF 和文件信息存入数据库
|
||||||
|
func (l *UploadFileLogic) saveFileInfoToDB(uid string, header *multipart.FileHeader, result types.File, originalDateTime, gpsString, locationString string, exif map[string]interface{}, faceId int64) error {
|
||||||
|
exifJSON, err := json.Marshal(exif)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("marshal exif failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
scaStorageInfo := &model.ScaStorageInfo{
|
||||||
|
UserID: uid,
|
||||||
|
Provider: result.FileType,
|
||||||
|
Bucket: result.TopCategory,
|
||||||
|
FileName: header.Filename,
|
||||||
|
FileSize: strconv.FormatInt(header.Size, 10),
|
||||||
|
FileType: result.FileType,
|
||||||
|
Landscape: result.Landscape,
|
||||||
|
Objects: strings.Join(result.ObjectArray, ", "),
|
||||||
|
Anime: strconv.FormatBool(result.IsAnime),
|
||||||
|
Category: result.TopCategory,
|
||||||
|
Screenshot: strconv.FormatBool(result.IsScreenshot),
|
||||||
|
OriginalTime: originalDateTime,
|
||||||
|
Gps: gpsString,
|
||||||
|
Location: locationString,
|
||||||
|
Exif: string(exifJSON),
|
||||||
|
FaceID: faceId,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = l.svcCtx.DB.ScaStorageInfo.Create(scaStorageInfo)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("create storage info failed")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -73,7 +73,7 @@ func (l *RefreshTokenLogic) RefreshToken(r *http.Request) (resp *types.RefreshTo
|
|||||||
GeneratedIP: redisTokenData.GeneratedIP,
|
GeneratedIP: redisTokenData.GeneratedIP,
|
||||||
UpdatedAt: time.Now().Format(constant.TimeFormat),
|
UpdatedAt: time.Now().Format(constant.TimeFormat),
|
||||||
}
|
}
|
||||||
err = l.svcCtx.RedisClient.Set(l.ctx, constant.UserTokenPrefix+refreshToken.UserID, redisToken, time.Hour*24*7).Err()
|
err = l.svcCtx.RedisClient.Set(l.ctx, constant.UserTokenPrefix+refreshToken.UserID, redisToken, time.Hour*24*3).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -8,8 +8,10 @@ import (
|
|||||||
"github.com/wenlng/go-captcha/v2/rotate"
|
"github.com/wenlng/go-captcha/v2/rotate"
|
||||||
"github.com/wenlng/go-captcha/v2/slide"
|
"github.com/wenlng/go-captcha/v2/slide"
|
||||||
"github.com/zeromicro/go-zero/rest"
|
"github.com/zeromicro/go-zero/rest"
|
||||||
|
"github.com/zeromicro/go-zero/zrpc"
|
||||||
sensitive "github.com/zmexing/go-sensitive-word"
|
sensitive "github.com/zmexing/go-sensitive-word"
|
||||||
"go.mongodb.org/mongo-driver/v2/mongo"
|
"go.mongodb.org/mongo-driver/v2/mongo"
|
||||||
|
"schisandra-album-cloud-microservices/app/aisvc/rpc/client/aiservice"
|
||||||
"schisandra-album-cloud-microservices/app/auth/api/internal/config"
|
"schisandra-album-cloud-microservices/app/auth/api/internal/config"
|
||||||
"schisandra-album-cloud-microservices/app/auth/api/internal/middleware"
|
"schisandra-album-cloud-microservices/app/auth/api/internal/middleware"
|
||||||
"schisandra-album-cloud-microservices/app/auth/model/mongodb"
|
"schisandra-album-cloud-microservices/app/auth/model/mongodb"
|
||||||
@@ -17,16 +19,18 @@ import (
|
|||||||
"schisandra-album-cloud-microservices/app/auth/model/mysql/query"
|
"schisandra-album-cloud-microservices/app/auth/model/mysql/query"
|
||||||
"schisandra-album-cloud-microservices/common/captcha/initialize"
|
"schisandra-album-cloud-microservices/common/captcha/initialize"
|
||||||
"schisandra-album-cloud-microservices/common/casbinx"
|
"schisandra-album-cloud-microservices/common/casbinx"
|
||||||
|
"schisandra-album-cloud-microservices/common/gao_map"
|
||||||
"schisandra-album-cloud-microservices/common/ip2region"
|
"schisandra-album-cloud-microservices/common/ip2region"
|
||||||
"schisandra-album-cloud-microservices/common/redisx"
|
"schisandra-album-cloud-microservices/common/redisx"
|
||||||
"schisandra-album-cloud-microservices/common/sensitivex"
|
"schisandra-album-cloud-microservices/common/sensitivex"
|
||||||
"schisandra-album-cloud-microservices/common/storage"
|
"schisandra-album-cloud-microservices/common/storage"
|
||||||
storage2 "schisandra-album-cloud-microservices/common/storage/manager"
|
"schisandra-album-cloud-microservices/common/storage/manager"
|
||||||
"schisandra-album-cloud-microservices/common/wechat_official"
|
"schisandra-album-cloud-microservices/common/wechat_official"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServiceContext struct {
|
type ServiceContext struct {
|
||||||
Config config.Config
|
Config config.Config
|
||||||
|
AiSvcRpc aiservice.AiService
|
||||||
SecurityHeadersMiddleware rest.Middleware
|
SecurityHeadersMiddleware rest.Middleware
|
||||||
CasbinVerifyMiddleware rest.Middleware
|
CasbinVerifyMiddleware rest.Middleware
|
||||||
AuthorizationMiddleware rest.Middleware
|
AuthorizationMiddleware rest.Middleware
|
||||||
@@ -40,7 +44,8 @@ type ServiceContext struct {
|
|||||||
RotateCaptcha rotate.Captcha
|
RotateCaptcha rotate.Captcha
|
||||||
SlideCaptcha slide.Captcha
|
SlideCaptcha slide.Captcha
|
||||||
Sensitive *sensitive.Manager
|
Sensitive *sensitive.Manager
|
||||||
StorageManager *storage2.Manager
|
StorageManager *manager.Manager
|
||||||
|
GaoMap *gao_map.AmapClient
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServiceContext(c config.Config) *ServiceContext {
|
func NewServiceContext(c config.Config) *ServiceContext {
|
||||||
@@ -63,5 +68,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
|||||||
MongoClient: mongodb.NewMongoDB(c.Mongo.Uri, c.Mongo.Username, c.Mongo.Password, c.Mongo.AuthSource, c.Mongo.Database),
|
MongoClient: mongodb.NewMongoDB(c.Mongo.Uri, c.Mongo.Username, c.Mongo.Password, c.Mongo.AuthSource, c.Mongo.Database),
|
||||||
Sensitive: sensitivex.NewSensitive(),
|
Sensitive: sensitivex.NewSensitive(),
|
||||||
StorageManager: storage.InitStorageManager(),
|
StorageManager: storage.InitStorageManager(),
|
||||||
|
GaoMap: gao_map.NewAmapClient(c.Map.Key, ""),
|
||||||
|
AiSvcRpc: aiservice.NewAiService(zrpc.MustNewClient(c.AiSvcRpc)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,13 +2,11 @@ package types
|
|||||||
|
|
||||||
// File represents a file uploaded by the user.
|
// File represents a file uploaded by the user.
|
||||||
type File struct {
|
type File struct {
|
||||||
UID string `json:"uid"`
|
FileType string `json:"fileType"`
|
||||||
FileName string `json:"fileName"`
|
IsAnime bool `json:"isAnime"`
|
||||||
FileType string `json:"fileType"`
|
ObjectArray []string `json:"objectArray"`
|
||||||
DetectionResult struct {
|
Landscape string `json:"landscape"`
|
||||||
IsAnime bool `json:"isAnime"`
|
TopCategory string `json:"topCategory"`
|
||||||
HasFace bool `json:"hasFace"`
|
IsScreenshot bool `json:"isScreenshot"`
|
||||||
ObjectArray []string `json:"objectArray"`
|
Exif any `json:"exif"`
|
||||||
Landscape string `json:"landscape"`
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -99,9 +99,41 @@ func main() {
|
|||||||
fieldOpts := []gen.ModelOpt{jsonField, idField, autoUpdateTimeField, autoCreateTimeField, softDeleteField, versionField}
|
fieldOpts := []gen.ModelOpt{jsonField, idField, autoUpdateTimeField, autoCreateTimeField, softDeleteField, versionField}
|
||||||
|
|
||||||
// 创建全部模型文件, 并覆盖前面创建的同名模型
|
// 创建全部模型文件, 并覆盖前面创建的同名模型
|
||||||
allModel := g.GenerateAllTable(fieldOpts...)
|
scaAuthMenu := g.GenerateModel("sca_auth_menu", fieldOpts...)
|
||||||
|
scaAuthPermissionRule := g.GenerateModel("sca_auth_permission_rule", fieldOpts...)
|
||||||
|
scaAuthRole := g.GenerateModel("sca_auth_role", fieldOpts...)
|
||||||
|
scaAuthUser := g.GenerateModel("sca_auth_user", fieldOpts...)
|
||||||
|
scaAuthUserDevice := g.GenerateModel("sca_auth_user_device", fieldOpts...)
|
||||||
|
scaAuthUserSocial := g.GenerateModel("sca_auth_user_social", fieldOpts...)
|
||||||
|
scaCommentReply := g.GenerateModel("sca_comment_reply", fieldOpts...)
|
||||||
|
scaCommentLikes := g.GenerateModel("sca_comment_likes", fieldOpts...)
|
||||||
|
scaMessageReport := g.GenerateModel("sca_message_report", fieldOpts...)
|
||||||
|
scaStorageConfig := g.GenerateModel("sca_storage_config", fieldOpts...)
|
||||||
|
scaStorageInfo := g.GenerateModel("sca_storage_info", fieldOpts...)
|
||||||
|
scaStorageTag := g.GenerateModel("sca_storage_tag", fieldOpts...)
|
||||||
|
scaStorageTagInfo := g.GenerateModel("sca_storage_tag_info", fieldOpts...)
|
||||||
|
scaUserFollows := g.GenerateModel("sca_user_follows", fieldOpts...)
|
||||||
|
scaUserLevel := g.GenerateModel("sca_user_level", fieldOpts...)
|
||||||
|
scaUserMessage := g.GenerateModel("sca_user_message", fieldOpts...)
|
||||||
|
|
||||||
g.ApplyBasic(allModel...)
|
g.ApplyBasic(
|
||||||
|
scaAuthMenu,
|
||||||
|
scaAuthPermissionRule,
|
||||||
|
scaAuthRole,
|
||||||
|
scaAuthUser,
|
||||||
|
scaAuthUserDevice,
|
||||||
|
scaAuthUserSocial,
|
||||||
|
scaCommentReply,
|
||||||
|
scaCommentLikes,
|
||||||
|
scaMessageReport,
|
||||||
|
scaStorageConfig,
|
||||||
|
scaStorageInfo,
|
||||||
|
scaStorageTag,
|
||||||
|
scaStorageTagInfo,
|
||||||
|
scaUserFollows,
|
||||||
|
scaUserLevel,
|
||||||
|
scaUserMessage,
|
||||||
|
)
|
||||||
|
|
||||||
g.Execute()
|
g.Execute()
|
||||||
}
|
}
|
||||||
|
@@ -14,24 +14,29 @@ const TableNameScaStorageInfo = "sca_storage_info"
|
|||||||
|
|
||||||
// ScaStorageInfo mapped from table <sca_storage_info>
|
// ScaStorageInfo mapped from table <sca_storage_info>
|
||||||
type ScaStorageInfo struct {
|
type ScaStorageInfo struct {
|
||||||
ID int64 `gorm:"column:id;type:bigint(20);primaryKey;autoIncrement:true;comment:主键;primary_key" json:"id,string"` // 主键
|
ID int64 `gorm:"column:id;type:bigint(20);primaryKey;autoIncrement:true;comment:主键;primary_key" json:"id,string"` // 主键
|
||||||
UserID string `gorm:"column:user_id;type:varchar(50);not null;comment:用户ID" json:"user_id"` // 用户ID
|
UserID string `gorm:"column:user_id;type:varchar(50);not null;comment:用户ID" json:"user_id"` // 用户ID
|
||||||
Storage string `gorm:"column:storage;type:varchar(50);comment:存储空间" json:"storage"` // 存储空间
|
Provider string `gorm:"column:provider;type:varchar(50);comment:供应商" json:"provider"` // 供应商
|
||||||
Bucket string `gorm:"column:bucket;type:varchar(50);comment:存储桶" json:"bucket"` // 存储桶
|
Bucket string `gorm:"column:bucket;type:varchar(50);comment:存储桶" json:"bucket"` // 存储桶
|
||||||
Type string `gorm:"column:type;type:varchar(50);comment:类型" json:"type"` // 类型
|
Path string `gorm:"column:path;type:varchar(255);comment:路径" json:"path"` // 路径
|
||||||
Path string `gorm:"column:path;type:varchar(255);comment:路径" json:"path"` // 路径
|
FileName string `gorm:"column:file_name;type:varchar(100);comment:文件名称" json:"file_name"` // 文件名称
|
||||||
FileName string `gorm:"column:file_name;type:varchar(100);comment:名称" json:"file_name"` // 名称
|
FileSize string `gorm:"column:file_size;type:varchar(50);comment:文件大小" json:"file_size"` // 文件大小
|
||||||
Category string `gorm:"column:category;type:varchar(50);comment:分类" json:"category"` // 分类
|
FileType string `gorm:"column:file_type;type:varchar(50);comment:文件类型" json:"file_type"` // 文件类型
|
||||||
Loaction string `gorm:"column:loaction;type:varchar(100);comment:地址" json:"loaction"` // 地址
|
Category string `gorm:"column:category;type:varchar(50);comment:分类" json:"category"` // 分类
|
||||||
Hash string `gorm:"column:hash;type:varchar(255);comment:哈希值" json:"hash"` // 哈希值
|
Tags string `gorm:"column:tags;type:varchar(255);comment:标签" json:"tags"` // 标签
|
||||||
Anime string `gorm:"column:anime;type:varchar(50);comment:是否是动漫图片" json:"anime"` // 是否是动漫图片
|
Location string `gorm:"column:location;type:varchar(100);comment:地址" json:"location"` // 地址
|
||||||
HasFace string `gorm:"column:has_face;type:varchar(50);comment:是否人像" json:"has_face"` // 是否人像
|
Hash string `gorm:"column:hash;type:varchar(255);comment:哈希值" json:"hash"` // 哈希值
|
||||||
FaceID int64 `gorm:"column:face_id;type:bigint(20);comment:人像ID" json:"face_id"` // 人像ID
|
Anime string `gorm:"column:anime;type:varchar(50);comment:是否是动漫图片" json:"anime"` // 是否是动漫图片
|
||||||
Landscape string `gorm:"column:landscape;type:varchar(50);comment:风景类型" json:"landscape"` // 风景类型
|
FaceID int64 `gorm:"column:face_id;type:bigint(20);comment:人像ID" json:"face_id"` // 人像ID
|
||||||
Objects string `gorm:"column:objects;type:varchar(50);comment:对象识别" json:"objects"` // 对象识别
|
Landscape string `gorm:"column:landscape;type:varchar(50);comment:风景类型" json:"landscape"` // 风景类型
|
||||||
CreatedAt time.Time `gorm:"column:created_at;type:timestamp;autoCreateTime;comment:创建时间" json:"created_at"` // 创建时间
|
Objects string `gorm:"column:objects;type:varchar(50);comment:对象识别" json:"objects"` // 对象识别
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp;autoUpdateTime;comment:更新时间" json:"updated_at"` // 更新时间
|
OriginalTime string `gorm:"column:original_time;type:varchar(50);comment:拍摄时间" json:"original_time"` // 拍摄时间
|
||||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp;comment:删除时间" json:"deleted_at"` // 删除时间
|
Gps string `gorm:"column:gps;type:varchar(255);comment:GPS" json:"gps"` // GPS
|
||||||
|
Screenshot string `gorm:"column:screenshot;type:varchar(50);comment:是否是截图" json:"screenshot"` // 是否是截图
|
||||||
|
Exif string `gorm:"column:exif;type:json;comment:exif 信息" json:"exif"` // exif 信息
|
||||||
|
CreatedAt time.Time `gorm:"column:created_at;type:timestamp;autoCreateTime;comment:创建时间" json:"created_at"` // 创建时间
|
||||||
|
UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp;autoUpdateTime;comment:更新时间" json:"updated_at"` // 更新时间
|
||||||
|
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp;comment:删除时间" json:"deleted_at"` // 删除时间
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName ScaStorageInfo's table name
|
// TableName ScaStorageInfo's table name
|
||||||
|
@@ -29,19 +29,24 @@ func newScaStorageInfo(db *gorm.DB, opts ...gen.DOOption) scaStorageInfo {
|
|||||||
_scaStorageInfo.ALL = field.NewAsterisk(tableName)
|
_scaStorageInfo.ALL = field.NewAsterisk(tableName)
|
||||||
_scaStorageInfo.ID = field.NewInt64(tableName, "id")
|
_scaStorageInfo.ID = field.NewInt64(tableName, "id")
|
||||||
_scaStorageInfo.UserID = field.NewString(tableName, "user_id")
|
_scaStorageInfo.UserID = field.NewString(tableName, "user_id")
|
||||||
_scaStorageInfo.Storage = field.NewString(tableName, "storage")
|
_scaStorageInfo.Provider = field.NewString(tableName, "provider")
|
||||||
_scaStorageInfo.Bucket = field.NewString(tableName, "bucket")
|
_scaStorageInfo.Bucket = field.NewString(tableName, "bucket")
|
||||||
_scaStorageInfo.Type = field.NewString(tableName, "type")
|
|
||||||
_scaStorageInfo.Path = field.NewString(tableName, "path")
|
_scaStorageInfo.Path = field.NewString(tableName, "path")
|
||||||
_scaStorageInfo.FileName = field.NewString(tableName, "file_name")
|
_scaStorageInfo.FileName = field.NewString(tableName, "file_name")
|
||||||
|
_scaStorageInfo.FileSize = field.NewString(tableName, "file_size")
|
||||||
|
_scaStorageInfo.FileType = field.NewString(tableName, "file_type")
|
||||||
_scaStorageInfo.Category = field.NewString(tableName, "category")
|
_scaStorageInfo.Category = field.NewString(tableName, "category")
|
||||||
_scaStorageInfo.Loaction = field.NewString(tableName, "loaction")
|
_scaStorageInfo.Tags = field.NewString(tableName, "tags")
|
||||||
|
_scaStorageInfo.Location = field.NewString(tableName, "location")
|
||||||
_scaStorageInfo.Hash = field.NewString(tableName, "hash")
|
_scaStorageInfo.Hash = field.NewString(tableName, "hash")
|
||||||
_scaStorageInfo.Anime = field.NewString(tableName, "anime")
|
_scaStorageInfo.Anime = field.NewString(tableName, "anime")
|
||||||
_scaStorageInfo.HasFace = field.NewString(tableName, "has_face")
|
|
||||||
_scaStorageInfo.FaceID = field.NewInt64(tableName, "face_id")
|
_scaStorageInfo.FaceID = field.NewInt64(tableName, "face_id")
|
||||||
_scaStorageInfo.Landscape = field.NewString(tableName, "landscape")
|
_scaStorageInfo.Landscape = field.NewString(tableName, "landscape")
|
||||||
_scaStorageInfo.Objects = field.NewString(tableName, "objects")
|
_scaStorageInfo.Objects = field.NewString(tableName, "objects")
|
||||||
|
_scaStorageInfo.OriginalTime = field.NewString(tableName, "original_time")
|
||||||
|
_scaStorageInfo.Gps = field.NewString(tableName, "gps")
|
||||||
|
_scaStorageInfo.Screenshot = field.NewString(tableName, "screenshot")
|
||||||
|
_scaStorageInfo.Exif = field.NewString(tableName, "exif")
|
||||||
_scaStorageInfo.CreatedAt = field.NewTime(tableName, "created_at")
|
_scaStorageInfo.CreatedAt = field.NewTime(tableName, "created_at")
|
||||||
_scaStorageInfo.UpdatedAt = field.NewTime(tableName, "updated_at")
|
_scaStorageInfo.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||||
_scaStorageInfo.DeletedAt = field.NewField(tableName, "deleted_at")
|
_scaStorageInfo.DeletedAt = field.NewField(tableName, "deleted_at")
|
||||||
@@ -54,25 +59,30 @@ func newScaStorageInfo(db *gorm.DB, opts ...gen.DOOption) scaStorageInfo {
|
|||||||
type scaStorageInfo struct {
|
type scaStorageInfo struct {
|
||||||
scaStorageInfoDo
|
scaStorageInfoDo
|
||||||
|
|
||||||
ALL field.Asterisk
|
ALL field.Asterisk
|
||||||
ID field.Int64 // 主键
|
ID field.Int64 // 主键
|
||||||
UserID field.String // 用户ID
|
UserID field.String // 用户ID
|
||||||
Storage field.String // 存储空间
|
Provider field.String // 供应商
|
||||||
Bucket field.String // 存储桶
|
Bucket field.String // 存储桶
|
||||||
Type field.String // 类型
|
Path field.String // 路径
|
||||||
Path field.String // 路径
|
FileName field.String // 文件名称
|
||||||
FileName field.String // 名称
|
FileSize field.String // 文件大小
|
||||||
Category field.String // 分类
|
FileType field.String // 文件类型
|
||||||
Loaction field.String // 地址
|
Category field.String // 分类
|
||||||
Hash field.String // 哈希值
|
Tags field.String // 标签
|
||||||
Anime field.String // 是否是动漫图片
|
Location field.String // 地址
|
||||||
HasFace field.String // 是否人像
|
Hash field.String // 哈希值
|
||||||
FaceID field.Int64 // 人像ID
|
Anime field.String // 是否是动漫图片
|
||||||
Landscape field.String // 风景类型
|
FaceID field.Int64 // 人像ID
|
||||||
Objects field.String // 对象识别
|
Landscape field.String // 风景类型
|
||||||
CreatedAt field.Time // 创建时间
|
Objects field.String // 对象识别
|
||||||
UpdatedAt field.Time // 更新时间
|
OriginalTime field.String // 拍摄时间
|
||||||
DeletedAt field.Field // 删除时间
|
Gps field.String // GPS
|
||||||
|
Screenshot field.String // 是否是截图
|
||||||
|
Exif field.String // exif 信息
|
||||||
|
CreatedAt field.Time // 创建时间
|
||||||
|
UpdatedAt field.Time // 更新时间
|
||||||
|
DeletedAt field.Field // 删除时间
|
||||||
|
|
||||||
fieldMap map[string]field.Expr
|
fieldMap map[string]field.Expr
|
||||||
}
|
}
|
||||||
@@ -91,19 +101,24 @@ func (s *scaStorageInfo) updateTableName(table string) *scaStorageInfo {
|
|||||||
s.ALL = field.NewAsterisk(table)
|
s.ALL = field.NewAsterisk(table)
|
||||||
s.ID = field.NewInt64(table, "id")
|
s.ID = field.NewInt64(table, "id")
|
||||||
s.UserID = field.NewString(table, "user_id")
|
s.UserID = field.NewString(table, "user_id")
|
||||||
s.Storage = field.NewString(table, "storage")
|
s.Provider = field.NewString(table, "provider")
|
||||||
s.Bucket = field.NewString(table, "bucket")
|
s.Bucket = field.NewString(table, "bucket")
|
||||||
s.Type = field.NewString(table, "type")
|
|
||||||
s.Path = field.NewString(table, "path")
|
s.Path = field.NewString(table, "path")
|
||||||
s.FileName = field.NewString(table, "file_name")
|
s.FileName = field.NewString(table, "file_name")
|
||||||
|
s.FileSize = field.NewString(table, "file_size")
|
||||||
|
s.FileType = field.NewString(table, "file_type")
|
||||||
s.Category = field.NewString(table, "category")
|
s.Category = field.NewString(table, "category")
|
||||||
s.Loaction = field.NewString(table, "loaction")
|
s.Tags = field.NewString(table, "tags")
|
||||||
|
s.Location = field.NewString(table, "location")
|
||||||
s.Hash = field.NewString(table, "hash")
|
s.Hash = field.NewString(table, "hash")
|
||||||
s.Anime = field.NewString(table, "anime")
|
s.Anime = field.NewString(table, "anime")
|
||||||
s.HasFace = field.NewString(table, "has_face")
|
|
||||||
s.FaceID = field.NewInt64(table, "face_id")
|
s.FaceID = field.NewInt64(table, "face_id")
|
||||||
s.Landscape = field.NewString(table, "landscape")
|
s.Landscape = field.NewString(table, "landscape")
|
||||||
s.Objects = field.NewString(table, "objects")
|
s.Objects = field.NewString(table, "objects")
|
||||||
|
s.OriginalTime = field.NewString(table, "original_time")
|
||||||
|
s.Gps = field.NewString(table, "gps")
|
||||||
|
s.Screenshot = field.NewString(table, "screenshot")
|
||||||
|
s.Exif = field.NewString(table, "exif")
|
||||||
s.CreatedAt = field.NewTime(table, "created_at")
|
s.CreatedAt = field.NewTime(table, "created_at")
|
||||||
s.UpdatedAt = field.NewTime(table, "updated_at")
|
s.UpdatedAt = field.NewTime(table, "updated_at")
|
||||||
s.DeletedAt = field.NewField(table, "deleted_at")
|
s.DeletedAt = field.NewField(table, "deleted_at")
|
||||||
@@ -123,22 +138,27 @@ func (s *scaStorageInfo) GetFieldByName(fieldName string) (field.OrderExpr, bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *scaStorageInfo) fillFieldMap() {
|
func (s *scaStorageInfo) fillFieldMap() {
|
||||||
s.fieldMap = make(map[string]field.Expr, 18)
|
s.fieldMap = make(map[string]field.Expr, 23)
|
||||||
s.fieldMap["id"] = s.ID
|
s.fieldMap["id"] = s.ID
|
||||||
s.fieldMap["user_id"] = s.UserID
|
s.fieldMap["user_id"] = s.UserID
|
||||||
s.fieldMap["storage"] = s.Storage
|
s.fieldMap["provider"] = s.Provider
|
||||||
s.fieldMap["bucket"] = s.Bucket
|
s.fieldMap["bucket"] = s.Bucket
|
||||||
s.fieldMap["type"] = s.Type
|
|
||||||
s.fieldMap["path"] = s.Path
|
s.fieldMap["path"] = s.Path
|
||||||
s.fieldMap["file_name"] = s.FileName
|
s.fieldMap["file_name"] = s.FileName
|
||||||
|
s.fieldMap["file_size"] = s.FileSize
|
||||||
|
s.fieldMap["file_type"] = s.FileType
|
||||||
s.fieldMap["category"] = s.Category
|
s.fieldMap["category"] = s.Category
|
||||||
s.fieldMap["loaction"] = s.Loaction
|
s.fieldMap["tags"] = s.Tags
|
||||||
|
s.fieldMap["location"] = s.Location
|
||||||
s.fieldMap["hash"] = s.Hash
|
s.fieldMap["hash"] = s.Hash
|
||||||
s.fieldMap["anime"] = s.Anime
|
s.fieldMap["anime"] = s.Anime
|
||||||
s.fieldMap["has_face"] = s.HasFace
|
|
||||||
s.fieldMap["face_id"] = s.FaceID
|
s.fieldMap["face_id"] = s.FaceID
|
||||||
s.fieldMap["landscape"] = s.Landscape
|
s.fieldMap["landscape"] = s.Landscape
|
||||||
s.fieldMap["objects"] = s.Objects
|
s.fieldMap["objects"] = s.Objects
|
||||||
|
s.fieldMap["original_time"] = s.OriginalTime
|
||||||
|
s.fieldMap["gps"] = s.Gps
|
||||||
|
s.fieldMap["screenshot"] = s.Screenshot
|
||||||
|
s.fieldMap["exif"] = s.Exif
|
||||||
s.fieldMap["created_at"] = s.CreatedAt
|
s.fieldMap["created_at"] = s.CreatedAt
|
||||||
s.fieldMap["updated_at"] = s.UpdatedAt
|
s.fieldMap["updated_at"] = s.UpdatedAt
|
||||||
s.fieldMap["deleted_at"] = s.DeletedAt
|
s.fieldMap["deleted_at"] = s.DeletedAt
|
||||||
|
5
common/constant/face_type.go
Normal file
5
common/constant/face_type.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package constant
|
||||||
|
|
||||||
|
const (
|
||||||
|
FaceTypeSample = "sample"
|
||||||
|
)
|
@@ -13,3 +13,7 @@ const (
|
|||||||
const (
|
const (
|
||||||
SystemApiNoncePrefix = "system:nonce:"
|
SystemApiNoncePrefix = "system:nonce:"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
FaceSamplePrefix = "face:sample:"
|
||||||
|
)
|
||||||
|
24
common/face_recognizer/face_recognizer.go
Normal file
24
common/face_recognizer/face_recognizer.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package face_recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Kagami/go-face"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewFaceRecognition creates a new instance of FaceRecognition
|
||||||
|
func NewFaceRecognition() *face.Recognizer {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
modelDir := filepath.Join(dir, "/resources/models/face")
|
||||||
|
rec, err := face.NewRecognizer(modelDir)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
//defer rec.Close()
|
||||||
|
return rec
|
||||||
|
}
|
84
common/gao_map/amap.go
Normal file
84
common/gao_map/amap.go
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
package gao_map
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/duke-git/lancet/v2/cryptor"
|
||||||
|
"github.com/duke-git/lancet/v2/maputil"
|
||||||
|
"github.com/duke-git/lancet/v2/netutil"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AmapClient struct {
|
||||||
|
key string
|
||||||
|
secret string
|
||||||
|
Location *Location
|
||||||
|
Direction *Direction
|
||||||
|
Place *Place
|
||||||
|
Weather *Weather
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAmapClient(key, secret string) *AmapClient {
|
||||||
|
amap := &AmapClient{key: key, secret: secret}
|
||||||
|
amap.Location = &Location{client: amap}
|
||||||
|
amap.Direction = &Direction{client: amap}
|
||||||
|
amap.Place = &Place{client: amap}
|
||||||
|
amap.Weather = &Weather{client: amap}
|
||||||
|
|
||||||
|
return amap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AmapClient) DoRequest(url, method string, params url.Values) (resp *http.Response, err error) {
|
||||||
|
params.Set("key", a.key)
|
||||||
|
if a.secret != "" {
|
||||||
|
params.Set("sig", makeSign(a, ¶ms))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch strings.ToUpper(method) {
|
||||||
|
case "GET":
|
||||||
|
resp, err = netutil.HttpGet(url, map[string]string{}, params)
|
||||||
|
case "POST":
|
||||||
|
resp, err = netutil.HttpPost(url, map[string]string{}, params)
|
||||||
|
default:
|
||||||
|
err = errors.New("unknown request method")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return nil, errors.New("amap request error: " + resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Info string `json:"info"`
|
||||||
|
Infocode string `json:"Infocode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeSign(client *AmapClient, values *url.Values) string {
|
||||||
|
keys := maputil.Keys(*values)
|
||||||
|
slice.SortBy(keys, func(a, b string) bool {
|
||||||
|
return a < b
|
||||||
|
})
|
||||||
|
|
||||||
|
data := slice.Map(keys, func(index int, item string) string {
|
||||||
|
return item + "=" + values.Get(item)
|
||||||
|
})
|
||||||
|
str := slice.Join(data, "&") + client.secret
|
||||||
|
return cryptor.Md5String(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkResponse(response Response) error {
|
||||||
|
if response.Status != "1" || response.Infocode != "10000" {
|
||||||
|
return errors.New("amap response error: " + response.Info)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
105
common/gao_map/beans.go
Normal file
105
common/gao_map/beans.go
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
package gao_map
|
||||||
|
|
||||||
|
type WeatherResponse struct {
|
||||||
|
Response
|
||||||
|
|
||||||
|
Lives []struct {
|
||||||
|
Adcode string `json:"adcode"`
|
||||||
|
City string `json:"city"`
|
||||||
|
Province string `json:"province"`
|
||||||
|
Weather string `json:"weather"`
|
||||||
|
Temperature string `json:"temperature"`
|
||||||
|
WindDirection string `json:"winddirection"`
|
||||||
|
WindPower string `json:"windpower"`
|
||||||
|
Humidity string `json:"humidity"`
|
||||||
|
ReportTime string `json:"reporttime"`
|
||||||
|
TemperatureFloat string `json:"temperature_float"`
|
||||||
|
HumidityFloat string `json:"humidity_float"`
|
||||||
|
} `json:"lives"`
|
||||||
|
Forecast []struct {
|
||||||
|
City string `json:"city"`
|
||||||
|
Adcode string `json:"adcode"`
|
||||||
|
Province string `json:"province"`
|
||||||
|
ReportTime string `json:"reporttime"`
|
||||||
|
Casts []struct {
|
||||||
|
Date string `json:"date"`
|
||||||
|
Week string `json:"week"`
|
||||||
|
DayWeather string `json:"dayweather"`
|
||||||
|
NightWeather string `json:"nightweather"`
|
||||||
|
DayTemp string `json:"daytemp"`
|
||||||
|
NightTemp string `json:"nighttemp"`
|
||||||
|
DayWind string `json:"daywind"`
|
||||||
|
NightWind string `json:"nightwind"`
|
||||||
|
DayPower string `json:"daypower"`
|
||||||
|
NightPower string `json:"nightpower"`
|
||||||
|
} `json:"casts"`
|
||||||
|
} `json:"forecast"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IpResponse struct {
|
||||||
|
Response
|
||||||
|
Province string `json:"province"`
|
||||||
|
City any `json:"city"`
|
||||||
|
Adcode string `json:"adcode"`
|
||||||
|
Rectangle string `json:"rectangle"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type District struct {
|
||||||
|
Citycode any `json:"citycode"`
|
||||||
|
Adcode string `json:"adcode"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Center string `json:"center"`
|
||||||
|
Level string `json:"level"`
|
||||||
|
Districts []*District `json:"districts"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AllDistrictResponse struct {
|
||||||
|
Response
|
||||||
|
|
||||||
|
Count int `json:"count,string"`
|
||||||
|
Districts []*District
|
||||||
|
}
|
||||||
|
|
||||||
|
type Geo struct {
|
||||||
|
FormattedAddress string `json:"formatted_address"`
|
||||||
|
Country string `json:"country"`
|
||||||
|
Province string `json:"province"`
|
||||||
|
Citycode string `json:"citycode"`
|
||||||
|
City any `json:"city"`
|
||||||
|
Adcode string `json:"adcode"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
Level string `json:"level"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeoResponse struct {
|
||||||
|
Response
|
||||||
|
|
||||||
|
Count string `json:"count"`
|
||||||
|
GeoCodes []Geo `json:"geocodes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReGeoResponse struct {
|
||||||
|
Response
|
||||||
|
|
||||||
|
ReGeoCode struct {
|
||||||
|
FormattedAddress string `json:"formatted_address"`
|
||||||
|
AddressComponent struct {
|
||||||
|
City any `json:"city"`
|
||||||
|
Country string `json:"country"`
|
||||||
|
Province string `json:"province"`
|
||||||
|
Citycode string `json:"citycode"`
|
||||||
|
District any `json:"district"`
|
||||||
|
Adcode string `json:"adcode"`
|
||||||
|
Township string `json:"township"`
|
||||||
|
Towncode string `json:"towncode"`
|
||||||
|
} `json:"addressComponent"`
|
||||||
|
} `json:"regeocode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReGeoRequest struct {
|
||||||
|
Location string
|
||||||
|
//PoiType string
|
||||||
|
Radius int
|
||||||
|
//Extensions string
|
||||||
|
//RoadLevel int
|
||||||
|
}
|
117
common/gao_map/direction.go
Normal file
117
common/gao_map/direction.go
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
package gao_map
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/duke-git/lancet/v2/convertor"
|
||||||
|
"github.com/duke-git/lancet/v2/netutil"
|
||||||
|
"github.com/duke-git/lancet/v2/strutil"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const drivingUrl = "https://restapi.amap.com/v5/direction/driving" //驾车
|
||||||
|
|
||||||
|
type Direction struct {
|
||||||
|
client *AmapClient
|
||||||
|
}
|
||||||
|
|
||||||
|
type DrivingRequest struct {
|
||||||
|
Origin string
|
||||||
|
Destination string
|
||||||
|
Strategy int
|
||||||
|
Waypoints []string
|
||||||
|
ShowFields string
|
||||||
|
}
|
||||||
|
|
||||||
|
type BicyclingRequest struct {
|
||||||
|
Origin string
|
||||||
|
Destination string
|
||||||
|
ShowFields string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Route struct {
|
||||||
|
Origin string `json:"origin"`
|
||||||
|
Destination string `json:"destination"`
|
||||||
|
Paths []struct {
|
||||||
|
Distance float64 `json:"distance,string"`
|
||||||
|
|
||||||
|
Steps []struct {
|
||||||
|
Cost struct {
|
||||||
|
Duration float64 `json:"duration,string"`
|
||||||
|
Tolls float64 `json:"tolls,string"`
|
||||||
|
TollsDistance float64 `json:"tolls_distance,string"`
|
||||||
|
} `json:"cost"`
|
||||||
|
Cities []struct {
|
||||||
|
Adcode string `json:"adcode"`
|
||||||
|
Citycode string `json:"citycode"`
|
||||||
|
City string `json:"city"`
|
||||||
|
} `json:"cities"`
|
||||||
|
Polyline string `json:"polyline"`
|
||||||
|
} `json:"steps"`
|
||||||
|
} `json:"paths"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DrivingResponse struct {
|
||||||
|
Response
|
||||||
|
|
||||||
|
Count int `json:"count,string"`
|
||||||
|
Route Route `json:"route"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BicyclingResponse struct {
|
||||||
|
Response
|
||||||
|
|
||||||
|
Count int `json:"count,string"`
|
||||||
|
Route Route `json:"route"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Direction) Bicycling(request *BicyclingRequest) (*BicyclingResponse, error) {
|
||||||
|
val := url.Values{}
|
||||||
|
val.Set("origin", request.Origin)
|
||||||
|
val.Set("destination", request.Destination)
|
||||||
|
if !strutil.IsBlank(request.ShowFields) {
|
||||||
|
val.Set("show_fields", request.ShowFields)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := d.client.DoRequest(drivingUrl, "POST", val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var data BicyclingResponse
|
||||||
|
|
||||||
|
if err = netutil.ParseHttpResponse(resp, &data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if err = checkResponse(data.Response); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &data, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Direction) Driving(request *DrivingRequest) (*DrivingResponse, error) {
|
||||||
|
val := url.Values{}
|
||||||
|
val.Set("origin", request.Origin)
|
||||||
|
val.Set("destination", request.Destination)
|
||||||
|
val.Set("strategy", convertor.ToString(request.Strategy))
|
||||||
|
if len(request.Waypoints) > 0 {
|
||||||
|
val.Set("waypoints", strings.Join(request.Waypoints, ";"))
|
||||||
|
}
|
||||||
|
if !strutil.IsBlank(request.ShowFields) {
|
||||||
|
val.Set("show_fields", request.ShowFields)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := d.client.DoRequest(drivingUrl, "POST", val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var data DrivingResponse
|
||||||
|
|
||||||
|
if err = netutil.ParseHttpResponse(resp, &data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if err = checkResponse(data.Response); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &data, err
|
||||||
|
}
|
109
common/gao_map/location.go
Normal file
109
common/gao_map/location.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
package gao_map
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/duke-git/lancet/v2/condition"
|
||||||
|
"github.com/duke-git/lancet/v2/convertor"
|
||||||
|
"github.com/duke-git/lancet/v2/netutil"
|
||||||
|
"github.com/duke-git/lancet/v2/validator"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ipUrl = "https://restapi.amap.com/v3/ip"
|
||||||
|
geoUrl = "https://restapi.amap.com/v3/geocode/geo"
|
||||||
|
regeoUrl = "https://restapi.amap.com/v3/geocode/regeo"
|
||||||
|
zoneUrl = "https://restapi.amap.com/v3/config/district"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Location struct {
|
||||||
|
client *AmapClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Location) ChinaDistricts() ([]*District, error) {
|
||||||
|
val := url.Values{}
|
||||||
|
val.Set("keywords", "中国")
|
||||||
|
val.Set("subdistrict", "3")
|
||||||
|
|
||||||
|
resp, err := l.client.DoRequest(zoneUrl, "GET", val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var data AllDistrictResponse
|
||||||
|
|
||||||
|
if err = netutil.ParseHttpResponse(resp, &data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if err = checkResponse(data.Response); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if data.Count <= 0 {
|
||||||
|
return nil, errors.New("no record")
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.Districts[0].Districts, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Location) IpLocation(ip string) (*IpResponse, error) {
|
||||||
|
val := url.Values{}
|
||||||
|
val.Set("ip", ip)
|
||||||
|
|
||||||
|
resp, err := l.client.DoRequest(ipUrl, "GET", val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var data IpResponse
|
||||||
|
|
||||||
|
if err = netutil.ParseHttpResponse(resp, &data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if err = checkResponse(data.Response); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Location) ReGeo(request *ReGeoRequest) (*ReGeoResponse, error) {
|
||||||
|
if validator.IsEmptyString(request.Location) {
|
||||||
|
return nil, errors.New("location can't empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
val := url.Values{}
|
||||||
|
val.Set("location", request.Location)
|
||||||
|
//if request.PoiType != "" {
|
||||||
|
// val.Set("poitype", request.PoiType)
|
||||||
|
//}
|
||||||
|
//val.Set("extensions", condition.Ternary(request.Extensions == "", "base", request.Extensions))
|
||||||
|
val.Set("radius", convertor.ToString(condition.Ternary(request.Radius == 0, 1000, request.Radius)))
|
||||||
|
//val.Set("roadlevel", convertor.ToString(request.RoadLevel))
|
||||||
|
|
||||||
|
resp, err := l.client.DoRequest(regeoUrl, "GET", val)
|
||||||
|
var data ReGeoResponse
|
||||||
|
|
||||||
|
if err = netutil.ParseHttpResponse(resp, &data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if err = checkResponse(data.Response); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Location) Geo(address, city string) (*GeoResponse, error) {
|
||||||
|
val := url.Values{}
|
||||||
|
val.Set("address", address)
|
||||||
|
if city != "" {
|
||||||
|
val.Set("city", city)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := l.client.DoRequest(geoUrl, "GET", val)
|
||||||
|
var data GeoResponse
|
||||||
|
|
||||||
|
if err = netutil.ParseHttpResponse(resp, &data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if err = checkResponse(data.Response); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &data, nil
|
||||||
|
}
|
50
common/gao_map/location_test.go
Normal file
50
common/gao_map/location_test.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package gao_map
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const key = ""
|
||||||
|
const secret = ""
|
||||||
|
|
||||||
|
func TestDistricts(t *testing.T) {
|
||||||
|
client := NewAmapClient(key, secret)
|
||||||
|
resp, err := client.Location.ChinaDistricts()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIp(t *testing.T) {
|
||||||
|
client := NewAmapClient(key, secret)
|
||||||
|
resp, err := client.Location.IpLocation("121.224.33.99")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Println(*resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGeo(t *testing.T) {
|
||||||
|
client := NewAmapClient(key, secret)
|
||||||
|
resp, err := client.Location.Geo("乐东公园一品墅", "")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Println(*resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReGeo(t *testing.T) {
|
||||||
|
client := NewAmapClient(key, secret)
|
||||||
|
request := ReGeoRequest{Location: "118.567824,29.306175"}
|
||||||
|
resp, err := client.Location.ReGeo(&request)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Println(*resp)
|
||||||
|
}
|
82
common/gao_map/place.go
Normal file
82
common/gao_map/place.go
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
package gao_map
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/duke-git/lancet/v2/condition"
|
||||||
|
"github.com/duke-git/lancet/v2/convertor"
|
||||||
|
"github.com/duke-git/lancet/v2/netutil"
|
||||||
|
"github.com/duke-git/lancet/v2/strutil"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
const placeUrl = "https://restapi.amap.com/v5/place/text" //驾车
|
||||||
|
|
||||||
|
type Place struct {
|
||||||
|
client *AmapClient
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlaceRequest struct {
|
||||||
|
Keywords string
|
||||||
|
Types string
|
||||||
|
Region string
|
||||||
|
CityLimit bool
|
||||||
|
PageSize int
|
||||||
|
PageNum int
|
||||||
|
ShowFields string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlaceResponse struct {
|
||||||
|
Response
|
||||||
|
|
||||||
|
Count int `json:"count,string"`
|
||||||
|
Pois []struct {
|
||||||
|
Address string `json:"address"`
|
||||||
|
Adcode string `json:"adcode"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Typecode string `json:"typecode"`
|
||||||
|
CityCode string `json:"citycode"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
Id string `json:"id"`
|
||||||
|
Business struct {
|
||||||
|
Tel string `json:"tel"`
|
||||||
|
Cost string `json:"cost"`
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
OpentimeToday string `json:"opentime_today"`
|
||||||
|
OpentimeWeek string `json:"opentime_week"`
|
||||||
|
} `json:"business"`
|
||||||
|
Photos []struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
} `json:"photos"`
|
||||||
|
} `json:"pois"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Place) Search(request *PlaceRequest) (*PlaceResponse, error) {
|
||||||
|
val := url.Values{}
|
||||||
|
val.Set("keywords", request.Keywords)
|
||||||
|
val.Set("types", request.Types)
|
||||||
|
if !strutil.IsBlank(request.Region) {
|
||||||
|
val.Set("region", request.Region)
|
||||||
|
}
|
||||||
|
val.Set("city_limit", condition.TernaryOperator(request.CityLimit, "1", "0"))
|
||||||
|
val.Set("page_size", convertor.ToString(condition.TernaryOperator(request.PageSize > 0, request.PageSize, 25)))
|
||||||
|
val.Set("page_num", convertor.ToString(condition.TernaryOperator(request.PageNum > 0, request.PageNum, 1)))
|
||||||
|
if !strutil.IsBlank(request.ShowFields) {
|
||||||
|
val.Set("show_fields", request.ShowFields)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := d.client.DoRequest(placeUrl, "GET", val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var data PlaceResponse
|
||||||
|
|
||||||
|
if err = netutil.ParseHttpResponse(resp, &data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if err = checkResponse(data.Response); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &data, err
|
||||||
|
}
|
32
common/gao_map/weather.go
Normal file
32
common/gao_map/weather.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package gao_map
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/duke-git/lancet/v2/netutil"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
const weatherInfoUrl = "https://restapi.amap.com/v3/weather/weatherInfo"
|
||||||
|
|
||||||
|
type Weather struct {
|
||||||
|
client *AmapClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Weather) Info(adcode, extensions string) (*WeatherResponse, error) {
|
||||||
|
val := url.Values{}
|
||||||
|
val.Set("city", adcode)
|
||||||
|
|
||||||
|
resp, err := w.client.DoRequest(weatherInfoUrl, "GET", val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var data WeatherResponse
|
||||||
|
|
||||||
|
if err = netutil.ParseHttpResponse(resp, &data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if err = checkResponse(data.Response); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &data, nil
|
||||||
|
}
|
@@ -1,7 +1,6 @@
|
|||||||
package ip2region
|
package ip2region
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
|
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -13,7 +12,6 @@ func NewIP2Region() *xdb.Searcher {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
fmt.Println(cwd)
|
|
||||||
dbPath := filepath.Join(cwd, "resources/ip2region", "ip2region.xdb")
|
dbPath := filepath.Join(cwd, "resources/ip2region", "ip2region.xdb")
|
||||||
cBuff, err := xdb.LoadContentFromFile(dbPath)
|
cBuff, err := xdb.LoadContentFromFile(dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -1,65 +0,0 @@
|
|||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SupportedHashFuncs 定义支持的哈希函数类型
|
|
||||||
var SupportedHashFuncs = map[string]func() hash.Hash{
|
|
||||||
"md5": crypto.MD5.New,
|
|
||||||
"sha1": crypto.SHA1.New,
|
|
||||||
"sha256": crypto.SHA256.New,
|
|
||||||
"sha512": crypto.SHA512.New,
|
|
||||||
}
|
|
||||||
|
|
||||||
// CalculateFileHash 根据指定的哈希算法计算文件的哈希值
|
|
||||||
func CalculateFileHash(filePath string, algorithm string) (string, error) {
|
|
||||||
// 获取对应的哈希函数
|
|
||||||
hashFunc, exists := SupportedHashFuncs[algorithm]
|
|
||||||
if !exists {
|
|
||||||
return "", fmt.Errorf("unsupported hash algorithm: %s", algorithm)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开文件
|
|
||||||
file, err := os.Open(filePath)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to open file: %w", err)
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
// 创建哈希对象
|
|
||||||
h := hashFunc()
|
|
||||||
|
|
||||||
// 计算哈希值
|
|
||||||
if _, err := io.Copy(h, file); err != nil {
|
|
||||||
return "", fmt.Errorf("failed to calculate hash: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 返回哈希值的十六进制字符串
|
|
||||||
return hex.EncodeToString(h.Sum(nil)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CalculateStreamHash 计算输入流的哈希值
|
|
||||||
func CalculateStreamHash(reader io.Reader, algorithm string) (string, error) {
|
|
||||||
// 获取对应的哈希函数
|
|
||||||
hashFunc, exists := SupportedHashFuncs[algorithm]
|
|
||||||
if !exists {
|
|
||||||
return "", fmt.Errorf("unsupported hash algorithm: %s", algorithm)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建哈希对象
|
|
||||||
h := hashFunc()
|
|
||||||
|
|
||||||
// 从输入流计算哈希值
|
|
||||||
if _, err := io.Copy(h, reader); err != nil {
|
|
||||||
return "", fmt.Errorf("failed to calculate hash: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 返回哈希值的十六进制字符串
|
|
||||||
return hex.EncodeToString(h.Sum(nil)), nil
|
|
||||||
}
|
|
24
common/utils/generate_image_hash.go
Normal file
24
common/utils/generate_image_hash.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/corona10/goimagehash"
|
||||||
|
"image"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CalculatePerceptualHash 计算感知哈希
|
||||||
|
func CalculatePerceptualHash(img image.Image) (string, error) {
|
||||||
|
hash, err := goimagehash.PerceptionHash(img)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return hash.ToString(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CalculateHash 计算平均哈希
|
||||||
|
func CalculateHash(img image.Image) (uint64, error) {
|
||||||
|
hash, err := goimagehash.AverageHash(img)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return hash.GetHash(), nil
|
||||||
|
}
|
@@ -1,8 +1,13 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
_ "image/jpeg" // 引入 jpeg 解码器
|
||||||
|
_ "image/png" // 引入 png 解码器
|
||||||
"io"
|
"io"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -88,3 +93,25 @@ func Base64ToBytes(base64Str string) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Base64ToImage 将 Base64 字符串转换为 image.Image 格式
|
||||||
|
func Base64ToImage(base64Str string) (image.Image, error) {
|
||||||
|
// 使用正则表达式去除前缀
|
||||||
|
re := regexp.MustCompile(`^data:image/([a-zA-Z]*);base64,`)
|
||||||
|
// 去除前缀部分
|
||||||
|
base64Str = re.ReplaceAllString(base64Str, "")
|
||||||
|
|
||||||
|
// 解码 Base64 字符串
|
||||||
|
data, err := base64.StdEncoding.DecodeString(base64Str)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode base64 string: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 image.Decode 解码字节数据
|
||||||
|
img, _, err := image.Decode(bytes.NewReader(data))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return img, nil
|
||||||
|
}
|
||||||
|
73
go.mod
73
go.mod
@@ -11,6 +11,7 @@ require (
|
|||||||
github.com/casbin/gorm-adapter/v3 v3.32.0
|
github.com/casbin/gorm-adapter/v3 v3.32.0
|
||||||
github.com/ccpwcn/kgo v1.2.8
|
github.com/ccpwcn/kgo v1.2.8
|
||||||
github.com/chenmingyong0423/go-mongox/v2 v2.0.0
|
github.com/chenmingyong0423/go-mongox/v2 v2.0.0
|
||||||
|
github.com/duke-git/lancet/v2 v2.3.4
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||||
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20241220152942-06eb5c6e8230
|
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20241220152942-06eb5c6e8230
|
||||||
@@ -31,6 +32,7 @@ require (
|
|||||||
golang.org/x/crypto v0.32.0
|
golang.org/x/crypto v0.32.0
|
||||||
golang.org/x/text v0.21.0
|
golang.org/x/text v0.21.0
|
||||||
google.golang.org/grpc v1.69.4
|
google.golang.org/grpc v1.69.4
|
||||||
|
google.golang.org/protobuf v1.36.3
|
||||||
gorm.io/driver/mysql v1.5.7
|
gorm.io/driver/mysql v1.5.7
|
||||||
gorm.io/gen v0.3.26
|
gorm.io/gen v0.3.26
|
||||||
gorm.io/gorm v1.25.12
|
gorm.io/gorm v1.25.12
|
||||||
@@ -41,6 +43,7 @@ require (
|
|||||||
require (
|
require (
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
github.com/ArtisanCloud/PowerSocialite/v3 v3.0.7 // indirect
|
github.com/ArtisanCloud/PowerSocialite/v3 v3.0.7 // indirect
|
||||||
|
github.com/Kagami/go-face v0.0.0-20210630145111-0c14797b4d0e // indirect
|
||||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||||
github.com/aymerick/douceur v0.2.0 // indirect
|
github.com/aymerick/douceur v0.2.0 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
@@ -52,26 +55,41 @@ require (
|
|||||||
github.com/clbanning/mxj v1.8.4 // indirect
|
github.com/clbanning/mxj v1.8.4 // indirect
|
||||||
github.com/clbanning/mxj/v2 v2.7.0 // indirect
|
github.com/clbanning/mxj/v2 v2.7.0 // indirect
|
||||||
github.com/cloudflare/circl v1.5.0 // indirect
|
github.com/cloudflare/circl v1.5.0 // indirect
|
||||||
|
github.com/coreos/go-semver v0.3.1 // indirect
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||||
|
github.com/corona10/goimagehash v1.1.0 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/dolthub/maphash v0.1.0 // indirect
|
github.com/dolthub/maphash v0.1.0 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
|
||||||
github.com/fatih/color v1.18.0 // indirect
|
github.com/fatih/color v1.18.0 // indirect
|
||||||
|
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||||
github.com/glebarez/go-sqlite v1.22.0 // indirect
|
github.com/glebarez/go-sqlite v1.22.0 // indirect
|
||||||
github.com/glebarez/sqlite v1.11.0 // indirect
|
github.com/glebarez/sqlite v1.11.0 // indirect
|
||||||
github.com/go-logr/logr v1.4.2 // indirect
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||||
|
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||||
|
github.com/go-openapi/swag v0.23.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.24.0 // indirect
|
github.com/go-playground/validator/v10 v10.24.0 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
|
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
|
||||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||||
|
github.com/golang/mock v1.6.0 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
|
github.com/google/gnostic-models v0.6.9 // indirect
|
||||||
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
|
github.com/google/pprof v0.0.0-20250120214715-4e5bb2051dab // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/gorilla/css v1.0.1 // indirect
|
github.com/gorilla/css v1.0.1 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
|
||||||
@@ -84,11 +102,13 @@ require (
|
|||||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/karlseguin/ccache/v3 v3.0.6 // indirect
|
github.com/karlseguin/ccache/v3 v3.0.6 // indirect
|
||||||
github.com/klauspost/compress v1.17.11 // indirect
|
github.com/klauspost/compress v1.17.11 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/lib/pq v1.10.7 // indirect
|
github.com/lib/pq v1.10.7 // indirect
|
||||||
|
github.com/mailru/easyjson v0.9.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/microsoft/go-mssqldb v1.8.0 // indirect
|
github.com/microsoft/go-mssqldb v1.8.0 // indirect
|
||||||
@@ -98,6 +118,7 @@ require (
|
|||||||
github.com/mozillazg/go-httpheader v0.4.0 // indirect
|
github.com/mozillazg/go-httpheader v0.4.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.22.2 // indirect
|
github.com/onsi/ginkgo/v2 v2.22.2 // indirect
|
||||||
github.com/openzipkin/zipkin-go v0.4.3 // indirect
|
github.com/openzipkin/zipkin-go v0.4.3 // indirect
|
||||||
github.com/orcaman/concurrent-map/v2 v2.0.1 // indirect
|
github.com/orcaman/concurrent-map/v2 v2.0.1 // indirect
|
||||||
@@ -106,28 +127,32 @@ require (
|
|||||||
github.com/pkg6/go-requests v0.2.3 // indirect
|
github.com/pkg6/go-requests v0.2.3 // indirect
|
||||||
github.com/prometheus/client_golang v1.20.5 // indirect
|
github.com/prometheus/client_golang v1.20.5 // indirect
|
||||||
github.com/prometheus/client_model v0.6.1 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
github.com/prometheus/common v0.61.0 // indirect
|
github.com/prometheus/common v0.62.0 // indirect
|
||||||
github.com/prometheus/procfs v0.15.1 // indirect
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
github.com/quic-go/qpack v0.5.1 // indirect
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
github.com/quic-go/quic-go v0.48.2 // indirect
|
github.com/quic-go/quic-go v0.48.2 // indirect
|
||||||
github.com/refraction-networking/utls v1.6.7 // indirect
|
github.com/refraction-networking/utls v1.6.7 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||||
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.1.2 // indirect
|
github.com/xdg-go/scram v1.1.2 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
|
go.etcd.io/etcd/api/v3 v3.5.17 // indirect
|
||||||
|
go.etcd.io/etcd/client/pkg/v3 v3.5.17 // indirect
|
||||||
|
go.etcd.io/etcd/client/v3 v3.5.17 // indirect
|
||||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.33.0 // indirect
|
go.opentelemetry.io/otel v1.34.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect
|
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.33.0 // indirect
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.34.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/zipkin v1.33.0 // indirect
|
go.opentelemetry.io/otel/exporters/zipkin v1.34.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.33.0 // indirect
|
go.opentelemetry.io/otel/metric v1.34.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk v1.33.0 // indirect
|
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.33.0 // indirect
|
go.opentelemetry.io/otel/trace v1.34.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||||
go.uber.org/mock v0.5.0 // indirect
|
go.uber.org/mock v0.5.0 // indirect
|
||||||
@@ -137,21 +162,33 @@ require (
|
|||||||
golang.org/x/image v0.23.0 // indirect
|
golang.org/x/image v0.23.0 // indirect
|
||||||
golang.org/x/mod v0.22.0 // indirect
|
golang.org/x/mod v0.22.0 // indirect
|
||||||
golang.org/x/net v0.34.0 // indirect
|
golang.org/x/net v0.34.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.25.0 // indirect
|
||||||
golang.org/x/sync v0.10.0 // indirect
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
golang.org/x/sys v0.29.0 // indirect
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
golang.org/x/time v0.8.0 // indirect
|
golang.org/x/term v0.28.0 // indirect
|
||||||
|
golang.org/x/time v0.9.0 // indirect
|
||||||
golang.org/x/tools v0.29.0 // indirect
|
golang.org/x/tools v0.29.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
|
||||||
google.golang.org/protobuf v1.36.2 // indirect
|
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||||
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
gorm.io/datatypes v1.2.5 // indirect
|
gorm.io/datatypes v1.2.5 // indirect
|
||||||
gorm.io/driver/postgres v1.5.11 // indirect
|
gorm.io/driver/postgres v1.5.11 // indirect
|
||||||
gorm.io/driver/sqlserver v1.5.4 // indirect
|
gorm.io/driver/sqlserver v1.5.4 // indirect
|
||||||
gorm.io/hints v1.1.2 // indirect
|
gorm.io/hints v1.1.2 // indirect
|
||||||
modernc.org/libc v1.61.7 // indirect
|
k8s.io/api v0.32.1 // indirect
|
||||||
|
k8s.io/apimachinery v0.32.1 // indirect
|
||||||
|
k8s.io/client-go v0.32.1 // indirect
|
||||||
|
k8s.io/klog/v2 v2.130.1 // indirect
|
||||||
|
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
|
||||||
|
k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
|
||||||
|
modernc.org/libc v1.61.9 // indirect
|
||||||
modernc.org/mathutil v1.7.1 // indirect
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
modernc.org/memory v1.8.1 // indirect
|
modernc.org/memory v1.8.2 // indirect
|
||||||
modernc.org/sqlite v1.34.4 // indirect
|
modernc.org/sqlite v1.34.5 // indirect
|
||||||
|
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
|
||||||
|
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
200
go.sum
200
go.sum
@@ -29,7 +29,13 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mx
|
|||||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
|
github.com/Kagami/go-face v0.0.0-20210630145111-0c14797b4d0e h1:lqIUFzxaqyYqUn4MhzAvSAh4wIte/iLNcIEWxpT/qbc=
|
||||||
|
github.com/Kagami/go-face v0.0.0-20210630145111-0c14797b4d0e/go.mod h1:9wdDJkRgo3SGTcFwbQ7elVIQhIr2bbBjecuY7VoqmPU=
|
||||||
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
|
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
|
||||||
|
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
|
||||||
|
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||||
|
github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0=
|
||||||
|
github.com/alicebob/miniredis/v2 v2.34.0/go.mod h1:kWShP4b58T1CW0Y5dViCd5ztzrDqRWqM3nksiyXk5s8=
|
||||||
github.com/aliyun/alibabacloud-oss-go-sdk-v2 v1.2.0 h1:gUWBCekWIFWYK7jXhRvPHIa7mRhJih2BGPjzvzodO18=
|
github.com/aliyun/alibabacloud-oss-go-sdk-v2 v1.2.0 h1:gUWBCekWIFWYK7jXhRvPHIa7mRhJih2BGPjzvzodO18=
|
||||||
github.com/aliyun/alibabacloud-oss-go-sdk-v2 v1.2.0/go.mod h1:FTzydeQVmR24FI0D6XWUOMKckjXehM/jgMn1xC+DA9M=
|
github.com/aliyun/alibabacloud-oss-go-sdk-v2 v1.2.0/go.mod h1:FTzydeQVmR24FI0D6XWUOMKckjXehM/jgMn1xC+DA9M=
|
||||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||||
@@ -69,19 +75,36 @@ github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyM
|
|||||||
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||||
github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
|
github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
|
||||||
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||||
|
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
|
||||||
|
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
|
github.com/corona10/goimagehash v1.1.0 h1:teNMX/1e+Wn/AYSbLHX8mj+mF9r60R1kBeqE9MkoYwI=
|
||||||
|
github.com/corona10/goimagehash v1.1.0/go.mod h1:VkvE0mLn84L4aF8vCb6mafVajEb6QYMHl2ZJLn0mOGI=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
|
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
|
||||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||||
github.com/dolthub/maphash v0.1.0 h1:bsQ7JsF4FkkWyrP3oCnFJgrCUAFbFf3kOl4L/QxPDyQ=
|
github.com/dolthub/maphash v0.1.0 h1:bsQ7JsF4FkkWyrP3oCnFJgrCUAFbFf3kOl4L/QxPDyQ=
|
||||||
github.com/dolthub/maphash v0.1.0/go.mod h1:gkg4Ch4CdCDu5h6PMriVLawB7koZ+5ijb9puGMV50a4=
|
github.com/dolthub/maphash v0.1.0/go.mod h1:gkg4Ch4CdCDu5h6PMriVLawB7koZ+5ijb9puGMV50a4=
|
||||||
|
github.com/duke-git/lancet/v2 v2.3.4 h1:8XGI7P9w+/GqmEBEXYaH/XuNiM0f4/90Ioti0IvYJls=
|
||||||
|
github.com/duke-git/lancet/v2 v2.3.4/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
|
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||||
|
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||||
|
github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU=
|
||||||
|
github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||||
|
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||||
|
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||||
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
|
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
|
||||||
@@ -90,10 +113,24 @@ github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GM
|
|||||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||||
github.com/go-bindata/go-bindata v3.1.2+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
|
github.com/go-bindata/go-bindata v3.1.2+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||||
|
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||||
|
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||||
|
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||||
|
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||||
|
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||||
|
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||||
|
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||||
|
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
|
||||||
|
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||||
|
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||||
|
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
@@ -105,8 +142,12 @@ github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1
|
|||||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
|
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
@@ -126,15 +167,24 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
|
|||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||||
|
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||||
|
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
|
||||||
|
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
|
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
|
||||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||||
|
github.com/google/pprof v0.0.0-20250120214715-4e5bb2051dab h1:FcOz+Hn0n9Ek4Ij6mvYUhvHGg0+EhV9ImR+PMmTP0S0=
|
||||||
|
github.com/google/pprof v0.0.0-20250120214715-4e5bb2051dab/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
@@ -144,6 +194,7 @@ github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
|
|||||||
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
|
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
|
||||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
|
||||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||||
@@ -175,14 +226,21 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
|
|||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/karlseguin/ccache/v3 v3.0.6 h1:6wC04CXSdptebuSUBgsQixNrrRMUdimtwmjlJUpCf/4=
|
github.com/karlseguin/ccache/v3 v3.0.6 h1:6wC04CXSdptebuSUBgsQixNrrRMUdimtwmjlJUpCf/4=
|
||||||
github.com/karlseguin/ccache/v3 v3.0.6/go.mod h1:b0qfdUOHl4vJgKFQN41paXIdBb3acAtyX2uWrBAZs1w=
|
github.com/karlseguin/ccache/v3 v3.0.6/go.mod h1:b0qfdUOHl4vJgKFQN41paXIdBb3acAtyX2uWrBAZs1w=
|
||||||
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
@@ -195,6 +253,10 @@ github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20241220152942-06eb5c6e8
|
|||||||
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20241220152942-06eb5c6e8230/go.mod h1:C5LA5UO2ZXJrLaPLYtE1wUJMiyd/nwWaCO5cw/2pSHs=
|
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20241220152942-06eb5c6e8230/go.mod h1:C5LA5UO2ZXJrLaPLYtE1wUJMiyd/nwWaCO5cw/2pSHs=
|
||||||
github.com/lxzan/gws v1.8.8 h1:st193ZG8qN8sSw8/g/UituFhs7etmKzS7jUqhijg5wM=
|
github.com/lxzan/gws v1.8.8 h1:st193ZG8qN8sSw8/g/UituFhs7etmKzS7jUqhijg5wM=
|
||||||
github.com/lxzan/gws v1.8.8/go.mod h1:FcGeRMB7HwGuTvMLR24ku0Zx0p6RXqeKASeMc4VYgi4=
|
github.com/lxzan/gws v1.8.8/go.mod h1:FcGeRMB7HwGuTvMLR24ku0Zx0p6RXqeKASeMc4VYgi4=
|
||||||
|
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||||
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
|
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||||
|
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
@@ -226,6 +288,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
|||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.4.1 h1:zwzjtX4uYyiaU02K5Ia3zSkpJZrByARkRB4V3YPrr0g=
|
github.com/nicksnyder/go-i18n/v2 v2.4.1 h1:zwzjtX4uYyiaU02K5Ia3zSkpJZrByARkRB4V3YPrr0g=
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.4.1/go.mod h1:++Pl70FR6Cki7hdzZRnEEqdc2dJt+SAGotyFg/SvZMk=
|
github.com/nicksnyder/go-i18n/v2 v2.4.1/go.mod h1:++Pl70FR6Cki7hdzZRnEEqdc2dJt+SAGotyFg/SvZMk=
|
||||||
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
|
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
|
||||||
@@ -259,6 +323,8 @@ github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p
|
|||||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
|
github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
|
||||||
github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
|
github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
|
||||||
|
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||||
|
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
@@ -275,6 +341,8 @@ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR
|
|||||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
@@ -297,6 +365,8 @@ github.com/wenlng/go-captcha-assets v1.0.1 h1:AdjRFMKmadPRWRTv0XEYfjDvcaayZ2yExI
|
|||||||
github.com/wenlng/go-captcha-assets v1.0.1/go.mod h1:yQqc7rRbxgLCg+tWtVp+7Y317D1wIZDan/yIwt8wSac=
|
github.com/wenlng/go-captcha-assets v1.0.1/go.mod h1:yQqc7rRbxgLCg+tWtVp+7Y317D1wIZDan/yIwt8wSac=
|
||||||
github.com/wenlng/go-captcha/v2 v2.0.2 h1:8twz6pI6xZwPvEGFezoFX395oFso1MuOlJt/tLiv7pk=
|
github.com/wenlng/go-captcha/v2 v2.0.2 h1:8twz6pI6xZwPvEGFezoFX395oFso1MuOlJt/tLiv7pk=
|
||||||
github.com/wenlng/go-captcha/v2 v2.0.2/go.mod h1:5hac1em3uXoyC5ipZ0xFv9umNM/waQvYAQdr0cx/h34=
|
github.com/wenlng/go-captcha/v2 v2.0.2/go.mod h1:5hac1em3uXoyC5ipZ0xFv9umNM/waQvYAQdr0cx/h34=
|
||||||
|
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||||
|
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||||
@@ -309,37 +379,72 @@ github.com/yitter/idgenerator-go v1.3.3 h1:i6rzmpbCL0vlmr/tuW5+lSQzNuDG9vYBjIYRv
|
|||||||
github.com/yitter/idgenerator-go v1.3.3/go.mod h1:VVjbqFjGUsIkaXVkXEdmx1LiXUL3K1NvyxWPJBPbBpE=
|
github.com/yitter/idgenerator-go v1.3.3/go.mod h1:VVjbqFjGUsIkaXVkXEdmx1LiXUL3K1NvyxWPJBPbBpE=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
|
||||||
|
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
|
||||||
github.com/zeromicro/go-zero v1.7.6 h1:SArK4xecdrpVY3ZFJcbc0IZCx+NuWyHNjCv9f1+Gwrc=
|
github.com/zeromicro/go-zero v1.7.6 h1:SArK4xecdrpVY3ZFJcbc0IZCx+NuWyHNjCv9f1+Gwrc=
|
||||||
github.com/zeromicro/go-zero v1.7.6/go.mod h1:SmGykRm5e0Z4CGNj+GaSKDffaHzQV56fel0FkymTLlE=
|
github.com/zeromicro/go-zero v1.7.6/go.mod h1:SmGykRm5e0Z4CGNj+GaSKDffaHzQV56fel0FkymTLlE=
|
||||||
github.com/zmexing/go-sensitive-word v1.3.0 h1:dB9S9kNklksOODGLLAov0RaVCwC2w9Kwxz6NZMdM6rk=
|
github.com/zmexing/go-sensitive-word v1.3.0 h1:dB9S9kNklksOODGLLAov0RaVCwC2w9Kwxz6NZMdM6rk=
|
||||||
github.com/zmexing/go-sensitive-word v1.3.0/go.mod h1:wkNIpkq1iPOe3l7l83zvnnV5mm20jfj2x8V8kjOTsUM=
|
github.com/zmexing/go-sensitive-word v1.3.0/go.mod h1:wkNIpkq1iPOe3l7l83zvnnV5mm20jfj2x8V8kjOTsUM=
|
||||||
|
go.etcd.io/etcd/api/v3 v3.5.15 h1:3KpLJir1ZEBrYuV2v+Twaa/e2MdDCEZ/70H+lzEiwsk=
|
||||||
|
go.etcd.io/etcd/api/v3 v3.5.15/go.mod h1:N9EhGzXq58WuMllgH9ZvnEr7SI9pS0k0+DHZezGp7jM=
|
||||||
|
go.etcd.io/etcd/api/v3 v3.5.17 h1:cQB8eb8bxwuxOilBpMJAEo8fAONyrdXTHUNcMd8yT1w=
|
||||||
|
go.etcd.io/etcd/api/v3 v3.5.17/go.mod h1:d1hvkRuXkts6PmaYk2Vrgqbv7H4ADfAKhyJqHNLJCB4=
|
||||||
|
go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5fWlA=
|
||||||
|
go.etcd.io/etcd/client/pkg/v3 v3.5.15/go.mod h1:mXDI4NAOwEiszrHCb0aqfAYNCrZP4e9hRca3d1YK8EU=
|
||||||
|
go.etcd.io/etcd/client/pkg/v3 v3.5.17 h1:XxnDXAWq2pnxqx76ljWwiQ9jylbpC4rvkAeRVOUKKVw=
|
||||||
|
go.etcd.io/etcd/client/pkg/v3 v3.5.17/go.mod h1:4DqK1TKacp/86nJk4FLQqo6Mn2vvQFBmruW3pP14H/w=
|
||||||
|
go.etcd.io/etcd/client/v3 v3.5.15 h1:23M0eY4Fd/inNv1ZfU3AxrbbOdW79r9V9Rl62Nm6ip4=
|
||||||
|
go.etcd.io/etcd/client/v3 v3.5.15/go.mod h1:CLSJxrYjvLtHsrPKsy7LmZEE+DK2ktfd2bN4RhBMwlU=
|
||||||
|
go.etcd.io/etcd/client/v3 v3.5.17 h1:o48sINNeWz5+pjy/Z0+HKpj/xSnBkuVhVvXkjEXbqZY=
|
||||||
|
go.etcd.io/etcd/client/v3 v3.5.17/go.mod h1:j2d4eXTHWkT2ClBgnnEPm/Wuu7jsqku41v9DZ3OtjQo=
|
||||||
go.mongodb.org/mongo-driver/v2 v2.0.0 h1:Jfd7XpdZa9yk3eY774bO7SWVb30noLSirL9nKTpavhI=
|
go.mongodb.org/mongo-driver/v2 v2.0.0 h1:Jfd7XpdZa9yk3eY774bO7SWVb30noLSirL9nKTpavhI=
|
||||||
go.mongodb.org/mongo-driver/v2 v2.0.0/go.mod h1:nSjmNq4JUstE8IRZKTktLgMHM4F1fccL6HGX1yh+8RA=
|
go.mongodb.org/mongo-driver/v2 v2.0.0/go.mod h1:nSjmNq4JUstE8IRZKTktLgMHM4F1fccL6HGX1yh+8RA=
|
||||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||||
go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
|
go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
|
||||||
go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
|
go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
|
||||||
|
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
|
||||||
|
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
|
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
|
||||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
|
go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 h1:BEj3SPM81McUZHYjRS5pEgNgnmzGJ5tRpU5krWnV8Bs=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0/go.mod h1:9cKLGBDzI/F3NoHLQGm4ZrYdIHsvGt6ej6hUowxY0J4=
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.33.0 h1:W5AWUn/IVe8RFb5pZx1Uh9Laf/4+Qmm4kJL5zPuvR+0=
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.33.0 h1:W5AWUn/IVe8RFb5pZx1Uh9Laf/4+Qmm4kJL5zPuvR+0=
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.33.0/go.mod h1:mzKxJywMNBdEX8TSJais3NnsVZUaJ+bAy6UxPTng2vk=
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.33.0/go.mod h1:mzKxJywMNBdEX8TSJais3NnsVZUaJ+bAy6UxPTng2vk=
|
||||||
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.34.0 h1:jBpDk4HAUsrnVO1FsfCfCOTEc/MkInJmvfCHYLFiT80=
|
||||||
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.34.0/go.mod h1:H9LUIM1daaeZaz91vZcfeM0fejXPmgCYE8ZhzqfJuiU=
|
||||||
go.opentelemetry.io/otel/exporters/zipkin v1.33.0 h1:aFexjEJIw5kVz6vQwnsqCG/nTV/UpsZh7MtQwGmH1eI=
|
go.opentelemetry.io/otel/exporters/zipkin v1.33.0 h1:aFexjEJIw5kVz6vQwnsqCG/nTV/UpsZh7MtQwGmH1eI=
|
||||||
go.opentelemetry.io/otel/exporters/zipkin v1.33.0/go.mod h1:aYsOzr/SZwZXJM6DJmSP/ST2P7MYxuc0R9RewkFVp9s=
|
go.opentelemetry.io/otel/exporters/zipkin v1.33.0/go.mod h1:aYsOzr/SZwZXJM6DJmSP/ST2P7MYxuc0R9RewkFVp9s=
|
||||||
|
go.opentelemetry.io/otel/exporters/zipkin v1.34.0 h1:GSjCkoYqsnvUMCjxF18j2tCWH8fhGZYjH3iYgechPTI=
|
||||||
|
go.opentelemetry.io/otel/exporters/zipkin v1.34.0/go.mod h1:h830hluwAqgSNnZbxL2rJhmAlE7/0SF9esoHVLU04Gc=
|
||||||
go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
|
go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
|
||||||
go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
|
go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
|
||||||
|
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
|
||||||
|
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
|
||||||
go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
|
go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
|
||||||
go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
|
go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
|
go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
|
go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
|
||||||
go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
|
go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
|
||||||
go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
|
go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
|
||||||
|
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
||||||
|
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
||||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
||||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||||
@@ -353,6 +458,8 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8
|
|||||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||||
@@ -368,14 +475,21 @@ golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbD
|
|||||||
golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs=
|
golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs=
|
||||||
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
|
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
|
||||||
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
|
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
@@ -388,13 +502,24 @@ golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
|||||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||||
|
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
|
||||||
|
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
|
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
|
||||||
|
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@@ -420,6 +545,8 @@ golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
|||||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||||
|
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||||
|
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
@@ -436,28 +563,39 @@ golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
|||||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
|
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||||
|
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
|
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
|
||||||
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
|
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw=
|
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 h1:3UsHvIr4Wc2aW4brOaSCmcxh9ksica6fHEr8P1XhkYw=
|
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
|
||||||
google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
|
google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
|
||||||
google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
||||||
google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
|
google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
|
||||||
google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
||||||
|
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||||
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
|
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
|
||||||
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
|
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
|
||||||
|
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||||
|
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
@@ -490,29 +628,63 @@ gorm.io/plugin/dbresolver v1.5.3 h1:wFwINGZZmttuu9h7XpvbDHd8Lf9bb8GNzp/NpAMV2wU=
|
|||||||
gorm.io/plugin/dbresolver v1.5.3/go.mod h1:TSrVhaUg2DZAWP3PrHlDlITEJmNOkL0tFTjvTEsQ4XE=
|
gorm.io/plugin/dbresolver v1.5.3/go.mod h1:TSrVhaUg2DZAWP3PrHlDlITEJmNOkL0tFTjvTEsQ4XE=
|
||||||
gorm.io/plugin/optimisticlock v1.1.3 h1:uFK8zz+Ln6ju3vGkTd1LY3xR2VBmMxjdU12KBb58PBA=
|
gorm.io/plugin/optimisticlock v1.1.3 h1:uFK8zz+Ln6ju3vGkTd1LY3xR2VBmMxjdU12KBb58PBA=
|
||||||
gorm.io/plugin/optimisticlock v1.1.3/go.mod h1:S+MH7qnHGQHxDBc9phjgN+DpNPn/qESd1q69fA3dtkg=
|
gorm.io/plugin/optimisticlock v1.1.3/go.mod h1:S+MH7qnHGQHxDBc9phjgN+DpNPn/qESd1q69fA3dtkg=
|
||||||
|
k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw=
|
||||||
|
k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80=
|
||||||
|
k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc=
|
||||||
|
k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k=
|
||||||
|
k8s.io/apimachinery v0.29.4 h1:RaFdJiDmuKs/8cm1M6Dh1Kvyh59YQFDcFuFTSmXes6Q=
|
||||||
|
k8s.io/apimachinery v0.29.4/go.mod h1:i3FJVwhvSp/6n8Fl4K97PJEP8C+MM+aoDq4+ZJBf70Y=
|
||||||
|
k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs=
|
||||||
|
k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||||
|
k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg=
|
||||||
|
k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0=
|
||||||
|
k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU=
|
||||||
|
k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg=
|
||||||
|
k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
|
||||||
|
k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
|
||||||
|
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||||
|
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||||
|
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
|
||||||
|
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
|
||||||
|
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg=
|
||||||
|
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas=
|
||||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
|
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
|
||||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||||
|
k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0=
|
||||||
|
k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||||
modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
|
modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
|
||||||
modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||||
modernc.org/ccgo/v4 v4.23.10 h1:DnDZT/H6TtoJvQmVf7d8W+lVqEZpIJY/+0ENFh1LIHE=
|
modernc.org/ccgo/v4 v4.23.13 h1:PFiaemQwE/jdwi8XEHyEV+qYWoIuikLP3T4rvDeJb00=
|
||||||
modernc.org/ccgo/v4 v4.23.10/go.mod h1:vdN4h2WR5aEoNondUx26K7G8X+nuBscYnAEWSRmN2/0=
|
modernc.org/ccgo/v4 v4.23.13/go.mod h1:vdN4h2WR5aEoNondUx26K7G8X+nuBscYnAEWSRmN2/0=
|
||||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||||
modernc.org/gc/v2 v2.6.1 h1:+Qf6xdG8l7B27TQ8D8lw/iFMUj1RXRBOuMUWziJOsk8=
|
modernc.org/gc/v2 v2.6.1 h1:+Qf6xdG8l7B27TQ8D8lw/iFMUj1RXRBOuMUWziJOsk8=
|
||||||
modernc.org/gc/v2 v2.6.1/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
modernc.org/gc/v2 v2.6.1/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||||
modernc.org/libc v1.61.7 h1:exz8rasFniviSgh3dH7QBnQHqYh9lolA5hVYfsiwkfo=
|
modernc.org/libc v1.61.9 h1:PLSBXVkifXGELtJ5BOnBUyAHr7lsatNwFU/RRo4kfJM=
|
||||||
modernc.org/libc v1.61.7/go.mod h1:xspSrXRNVSfWfcfqgvZDVe/Hw5kv4FVC6IRfoms5v/0=
|
modernc.org/libc v1.61.9/go.mod h1:61xrnzk/aR8gr5bR7Uj/lLFLuXu2/zMpIjcry63Eumk=
|
||||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||||
modernc.org/memory v1.8.1 h1:HS1HRg1jEohnuONobEq2WrLEhLyw8+J42yLFTnllm2A=
|
modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI=
|
||||||
modernc.org/memory v1.8.1/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU=
|
modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU=
|
||||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||||
modernc.org/sqlite v1.34.4 h1:sjdARozcL5KJBvYQvLlZEmctRgW9xqIZc2ncN7PU0P8=
|
modernc.org/sqlite v1.34.5 h1:Bb6SR13/fjp15jt70CL4f18JIN7p7dnMExd+UFnF15g=
|
||||||
modernc.org/sqlite v1.34.4/go.mod h1:3QQFCG2SEMtc2nv+Wq4cQCH7Hjcg+p/RMlS1XK+zwbk=
|
modernc.org/sqlite v1.34.5/go.mod h1:YLuNmX9NKs8wRNK2ko1LW1NGYcc9FkBO69JOt1AR9JE=
|
||||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
|
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||||
|
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||||
|
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||||
|
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk=
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
|
||||||
|
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||||
|
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||||
|
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||||
|
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||||
|
Reference in New Issue
Block a user