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 }