first commit
This commit is contained in:
185
pkg/datasource/fields/field.go
Normal file
185
pkg/datasource/fields/field.go
Normal file
@ -0,0 +1,185 @@
|
||||
package fields
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
const (
|
||||
timeFormat = "2006-01-02 15:04:05"
|
||||
dateFormat = "2006-01-02"
|
||||
)
|
||||
|
||||
type STPoint struct {
|
||||
Lng float64
|
||||
Lat float64
|
||||
}
|
||||
|
||||
type Time time.Time
|
||||
|
||||
func (t *Time) UnmarshalJSON(data []byte) (err error) {
|
||||
now, err := time.ParseInLocation(`"`+timeFormat+`"`, string(data), time.Local)
|
||||
*t = Time(now)
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Time) MarshalJSON() ([]byte, error) {
|
||||
b := make([]byte, 0, len(timeFormat)+2)
|
||||
b = append(b, '"')
|
||||
b = time.Time(*t).AppendFormat(b, timeFormat)
|
||||
b = append(b, '"')
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (t *Time) String() string {
|
||||
return time.Time(*t).Format(timeFormat)
|
||||
}
|
||||
|
||||
func (t *Time) Value() (driver.Value, error) {
|
||||
return time.Time(*t).Format(timeFormat), nil
|
||||
}
|
||||
|
||||
func (t *Time) Time() time.Time {
|
||||
return time.Time(*t)
|
||||
}
|
||||
|
||||
func NowTime() Time {
|
||||
return Time(time.Now())
|
||||
}
|
||||
|
||||
type Date time.Time
|
||||
|
||||
func (t *Date) UnmarshalJSON(data []byte) (err error) {
|
||||
now, err := time.ParseInLocation(`"`+dateFormat+`"`, string(data), time.Local)
|
||||
*t = Date(now)
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Date) MarshalJSON() ([]byte, error) {
|
||||
b := make([]byte, 0, len(dateFormat)+2)
|
||||
b = append(b, '"')
|
||||
b = time.Time(*t).AppendFormat(b, dateFormat)
|
||||
b = append(b, '"')
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (t *Date) String() string {
|
||||
return time.Time(*t).Format(dateFormat)
|
||||
}
|
||||
|
||||
func (t *Date) FromStr(s string) error {
|
||||
now, err := time.ParseInLocation(dateFormat, string(s), time.Local)
|
||||
*t = Date(now)
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *Date) Time() time.Time {
|
||||
return time.Time(*t)
|
||||
}
|
||||
|
||||
type Month time.Time
|
||||
|
||||
const (
|
||||
monthFormat = "2006-01"
|
||||
)
|
||||
|
||||
func (t *Month) UnmarshalJSON(data []byte) (err error) {
|
||||
if len(data) <= 2 {
|
||||
return nil
|
||||
}
|
||||
now, err := time.ParseInLocation(`"`+monthFormat+`"`, string(data), time.Local)
|
||||
*t = Month(now)
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Month) MarshalJSON() ([]byte, error) {
|
||||
b := make([]byte, 0, len(monthFormat)+2)
|
||||
b = append(b, '"')
|
||||
b = time.Time(*t).AppendFormat(b, monthFormat)
|
||||
b = append(b, '"')
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (t *Month) String() string {
|
||||
return time.Time(*t).Format(monthFormat)
|
||||
}
|
||||
|
||||
type Point struct {
|
||||
Lat float64 `json:"lat"`
|
||||
Lng float64 `json:"lng"`
|
||||
}
|
||||
|
||||
func (p Point) Value() (driver.Value, error) {
|
||||
return fmt.Sprintf("POINT(%f %f)", p.Lng, p.Lat), nil
|
||||
}
|
||||
|
||||
func (p *Point) Scan(src interface{}) error {
|
||||
switch src.(type) {
|
||||
case []byte:
|
||||
var b = src.([]byte)
|
||||
if len(b) != 25 {
|
||||
return errors.New(fmt.Sprintf("Expected []bytes with length 25, got %d", len(b)))
|
||||
}
|
||||
var longitude float64
|
||||
var latitude float64
|
||||
buf := bytes.NewReader(b[9:17])
|
||||
err := binary.Read(buf, binary.LittleEndian, &longitude)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf = bytes.NewReader(b[17:25])
|
||||
err = binary.Read(buf, binary.LittleEndian, &latitude)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.Lng = longitude
|
||||
p.Lat = latitude
|
||||
default:
|
||||
return errors.New(fmt.Sprintf("Expected []byte for Location type, got %T", src))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (loc Point) GormDataType() string {
|
||||
return "point"
|
||||
}
|
||||
|
||||
func (loc Point) GormValue(ctx context.Context, db *gorm.DB) clause.Expr {
|
||||
return clause.Expr{
|
||||
SQL: "ST_PointFromText(?)",
|
||||
Vars: []interface{}{fmt.Sprintf("POINT(%f %f)", loc.Lng, loc.Lat)},
|
||||
}
|
||||
}
|
||||
|
||||
func (p Point) DistanceTo(other Point) float64 {
|
||||
const earthRadius = 6371000 // 地球半径,单位为米
|
||||
|
||||
lat1 := p.Lat * math.Pi / 180
|
||||
lng1 := p.Lng * math.Pi / 180
|
||||
lat2 := other.Lat * math.Pi / 180
|
||||
lng2 := other.Lng * math.Pi / 180
|
||||
|
||||
dlat := lat2 - lat1
|
||||
dlng := lng2 - lng1
|
||||
|
||||
a := math.Sin(dlat/2)*math.Sin(dlat/2) +
|
||||
math.Cos(lat1)*math.Cos(lat2)*math.Sin(dlng/2)*math.Sin(dlng/2)
|
||||
c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
|
||||
|
||||
distance := earthRadius * c
|
||||
// point1 := Point{Lat: 40.7128, Lng: -74.0060}
|
||||
// point2 := Point{Lat: 34.0522, Lng: -118.2437}
|
||||
// distance := point1.DistanceTo(point2)
|
||||
// fmt.Printf("Distance between points: %.2f km\n", distance)
|
||||
// fmt.Printf("Distance between points: %d meters\n", distance)
|
||||
return distance
|
||||
}
|
||||
128
pkg/datasource/mysql.go
Normal file
128
pkg/datasource/mysql.go
Normal file
@ -0,0 +1,128 @@
|
||||
package datasource
|
||||
|
||||
import (
|
||||
"servicebase/pkg/log"
|
||||
"servicebase/pkg/repo"
|
||||
"servicebase/pkg/utils"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
"gorm.io/plugin/dbresolver"
|
||||
)
|
||||
|
||||
type BaseModel struct {
|
||||
ID int `gorm:"primaryKey" json:"id"`
|
||||
CreateTime utils.Time `gorm:"type:datetime;index;not null" json:"createTime"`
|
||||
}
|
||||
|
||||
func (model *BaseModel) BeforeCreate(tx *gorm.DB) (err error) {
|
||||
model.CreateTime.Time = time.Now()
|
||||
return
|
||||
}
|
||||
|
||||
var DB *gorm.DB
|
||||
|
||||
type MySQLStarter struct {
|
||||
}
|
||||
|
||||
func (s *MySQLStarter) Init() error {
|
||||
InitMySQl()
|
||||
return nil
|
||||
}
|
||||
|
||||
func InitMySQl() {
|
||||
dsn := viper.GetString("db.connectString")
|
||||
log.InfoF("init db config with: %s", dsn)
|
||||
var e error
|
||||
if DB, e = gorm.Open(mysql.New(mysql.Config{
|
||||
DriverName: "",
|
||||
ServerVersion: "",
|
||||
DSN: dsn,
|
||||
Conn: nil,
|
||||
SkipInitializeWithVersion: false,
|
||||
DefaultStringSize: 255,
|
||||
DefaultDatetimePrecision: nil,
|
||||
DisableDatetimePrecision: true,
|
||||
DontSupportRenameIndex: false,
|
||||
DontSupportRenameColumn: true,
|
||||
DontSupportForShareClause: false,
|
||||
// DontSupportNullAsDefaultValue: false,
|
||||
}), &gorm.Config{
|
||||
SkipDefaultTransaction: false,
|
||||
NamingStrategy: schema.NamingStrategy{
|
||||
TablePrefix: "", // table name prefix, table for `User` would be `t_users`
|
||||
SingularTable: true, // use singular table name, table for `User` would be `user` with this option enabled
|
||||
NoLowerCase: false, // skip the snake_casing of names
|
||||
NameReplacer: strings.NewReplacer("PID", "pid"), // use name replacer to change struct/field name before convert it to db name
|
||||
},
|
||||
FullSaveAssociations: false,
|
||||
Logger: &log.GLog{}, //
|
||||
NowFunc: func() time.Time {
|
||||
return time.Now().Local()
|
||||
},
|
||||
DryRun: false,
|
||||
PrepareStmt: false, // 执行任何 SQL 时都会创建一个 prepared statement 并将其缓存,以提高后续的效率
|
||||
DisableAutomaticPing: false, // 在完成初始化后,GORM 会自动 ping 数据库以检查数据库的可用性
|
||||
DisableForeignKeyConstraintWhenMigrating: true, // 在 AutoMigrate 或 CreateTable 时,GORM 会自动创建外键约束,若要禁用该特性,可将其设置为 true
|
||||
DisableNestedTransaction: false, // 嵌套事务
|
||||
AllowGlobalUpdate: false, // true = 启用全局 update/delete
|
||||
QueryFields: false,
|
||||
CreateBatchSize: 0,
|
||||
ClauseBuilders: nil,
|
||||
ConnPool: nil,
|
||||
Dialector: nil,
|
||||
Plugins: nil,
|
||||
}); e != nil {
|
||||
panic("failed to connect database")
|
||||
}
|
||||
|
||||
_ = DB.Use(dbresolver.Register(dbresolver.Config{
|
||||
Replicas: []gorm.Dialector{mysql.Open(dsn)},
|
||||
Policy: dbresolver.RandomPolicy{},
|
||||
}).Register(dbresolver.Config{
|
||||
Replicas: []gorm.Dialector{mysql.Open(dsn)},
|
||||
} /* &model.User{} */))
|
||||
|
||||
sqlDB, _ := DB.DB()
|
||||
// SetMaxIdleConns 用于设置连接池中空闲连接的最大数量。
|
||||
sqlDB.SetMaxIdleConns(20)
|
||||
// SetMaxOpenConns 设置打开数据库连接的最大数量。
|
||||
sqlDB.SetMaxOpenConns(100)
|
||||
// SetConnMaxLifetime 设置了连接可复用的最大时间。
|
||||
sqlDB.SetConnMaxLifetime(time.Hour * 24)
|
||||
|
||||
// log.InfoF("gorm metrics at: %d", viper.GetUint32("db.metrics.port"))
|
||||
// _ = DB.Use(prometheus.New(prometheus.Config{
|
||||
// DBName: "db_data-front", // 使用 `DBName` 作为指标 label
|
||||
// RefreshInterval: 10, // 指标刷新频率(默认为 15 秒)
|
||||
// PushAddr: "", // 如果配置了 `PushAddr`,则推送指标
|
||||
// StartServer: false, // 启用一个 http 服务来暴露指标
|
||||
// //HTTPServerPort: viper.GetUint32("db.metrics.port"), // 配置 http 服务监听端口,默认端口为 8080 (如果您配置了多个,只有第一个 `HTTPServerPort` 会被使用)
|
||||
// MetricsCollector: []prometheus.MetricsCollector{
|
||||
// &prometheus.MySQL{
|
||||
// // 指标名前缀,默认为 `gorm_status_`
|
||||
// // 例如: Threads_running 的指标名就是 `gorm_status_Threads_running`
|
||||
// Prefix: "gorm_status_",
|
||||
// // 拉取频率,默认使用 Prometheus 的 RefreshInterval
|
||||
// Interval: 30,
|
||||
// // 从 SHOW STATUS 选择变量变量,如果不设置,则使用全部的状态变量
|
||||
// VariableNames: []string{"Threads_running"},
|
||||
// },
|
||||
// }, // 用户自定义指标
|
||||
// }))
|
||||
|
||||
//_ = DB.Use(tracing.NewPlugin(tracing.WithoutMetrics()))
|
||||
|
||||
autoMigrate()
|
||||
|
||||
repo.SetDefault(DB)
|
||||
}
|
||||
|
||||
func autoMigrate() {
|
||||
//_ = DB.AutoMigrate(&model.User{})
|
||||
//_ = DB.AutoMigrate(&model.LeaseType{})
|
||||
}
|
||||
Reference in New Issue
Block a user