Files
2025-11-19 14:24:13 +08:00

186 lines
3.9 KiB
Go

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 any) 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: []any{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
}