added manager, driver, order, route, vehicle, warehouse endpoints,models and migrations.

This commit is contained in:
2026-03-23 18:28:01 +05:00
parent f710cda3f3
commit 6e8750a566
22 changed files with 2463 additions and 111 deletions

1
go.mod
View File

@@ -25,6 +25,7 @@ require (
)
require (
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect

6
go.sum
View File

@@ -1,3 +1,7 @@
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
@@ -50,6 +54,7 @@ github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpbo=
@@ -90,6 +95,7 @@ github.com/samber/slog-chi v1.19.0 h1:fl4qH5Hhk7feHtyp4CxJUt7U1TqjPrZ1uueDW9D+Cp
github.com/samber/slog-chi v1.19.0/go.mod h1:a1iIuofF2gS1ii8aXIQhC6TEguLOhOvSM958fY5hToU=
github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE=
github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
package handler
import "net/http"
func (s *Server) ListDrivers(w http.ResponseWriter, r *http.Request) {}
func (s *Server) CreateDriver(w http.ResponseWriter, r *http.Request) {}
func (s *Server) GetDriver(w http.ResponseWriter, r *http.Request, slug string) {}
func (s *Server) UpdateDriver(w http.ResponseWriter, r *http.Request, slug string) {}
func (s *Server) DeleteDriver(w http.ResponseWriter, r *http.Request, slug string) {}

View File

@@ -0,0 +1,11 @@
package handler
import "net/http"
func (s *Server) ListManagers(w http.ResponseWriter, r *http.Request) {}
func (s *Server) CreateManager(w http.ResponseWriter, r *http.Request) {}
func (s *Server) GetManager(w http.ResponseWriter, r *http.Request, slug string) {}
func (s *Server) DeleteManager(w http.ResponseWriter, r *http.Request, slug string) {}

17
internal/handler/order.go Normal file
View File

@@ -0,0 +1,17 @@
package handler
import (
"net/http"
openapi_types "github.com/oapi-codegen/runtime/types"
)
func (s *Server) ListOrders(w http.ResponseWriter, r *http.Request) {}
func (s *Server) CreateOrder(w http.ResponseWriter, r *http.Request) {}
func (s *Server) GetOrder(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) {}
func (s *Server) CancelOrder(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) {}
func (s *Server) UpdateOrderStatus(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) {}

11
internal/handler/route.go Normal file
View File

@@ -0,0 +1,11 @@
package handler
import (
"net/http"
openapi_types "github.com/oapi-codegen/runtime/types"
)
func (s *Server) GetRoute(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) {}
func (s *Server) RouteWebSocket(w http.ResponseWriter, r *http.Request, id openapi_types.UUID) {}

View File

@@ -47,7 +47,7 @@ func (s *Server) AuthLogin(w http.ResponseWriter, r *http.Request) {
}
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.Password)); err != nil {
slog.WarnContext(ctx, "Failed login to account with", slog.String("email:", string(req.Email)), slog.String("password from request", req.Password))
slog.WarnContext(ctx, "Failed login to account with", slog.String("email:", string(req.Email)))
s.JSON(w, r, http.StatusBadRequest, "Неверный пароль", "error")
return
}
@@ -207,7 +207,7 @@ func (s *Server) DeleteMe(w http.ResponseWriter, r *http.Request) {
}
jwt := r.Header.Get("Authorization")
tokenKey := "access_token" + jwt
tokenKey := "access_token:" + jwt
if err := s.Redis.Del(ctx, tokenKey).Err(); err != nil {
slog.ErrorContext(ctx, "Error while deleting access token from redis", slog.String("token", jwt))
s.JSON(w, r, http.StatusInternalServerError, "Internal server error", "error")

View File

@@ -0,0 +1,13 @@
package handler
import "net/http"
func (s *Server) ListVehicles(w http.ResponseWriter, r *http.Request) {}
func (s *Server) CreateVehicle(w http.ResponseWriter, r *http.Request) {}
func (s *Server) GetVehicle(w http.ResponseWriter, r *http.Request, slug string) {}
func (s *Server) UpdateVehicle(w http.ResponseWriter, r *http.Request, slug string) {}
func (s *Server) DeleteVehicle(w http.ResponseWriter, r *http.Request, slug string) {}

View File

@@ -0,0 +1,13 @@
package handler
import "net/http"
func (s *Server) ListWarehouses(w http.ResponseWriter, r *http.Request) {}
func (s *Server) CreateWarehouse(w http.ResponseWriter, r *http.Request) {}
func (s *Server) GetWarehouse(w http.ResponseWriter, r *http.Request, slug string) {}
func (s *Server) UpdateWarehouse(w http.ResponseWriter, r *http.Request, slug string) {}
func (s *Server) DeleteWarehouse(w http.ResponseWriter, r *http.Request, slug string) {}

18
internal/models/driver.go Normal file
View File

@@ -0,0 +1,18 @@
package models
import (
"time"
"github.com/google/uuid"
)
type Driver struct {
ID uuid.UUID `db:"id"`
UserID uuid.UUID `db:"user_id"`
VehicleID *uuid.UUID `db:"vehicle_id"`
LicenseNumber string `db:"license_number"`
LicenseExpiry time.Time `db:"license_expiry"`
Rating float64 `db:"rating"`
Slug string `db:"slug"`
Status string `db:"status"` // available, on_route, off_duty
}

View File

@@ -0,0 +1,10 @@
package models
import "github.com/google/uuid"
type Manager struct {
ID uuid.UUID `db:"id"`
UserID uuid.UUID `db:"user_id"`
WarehouseID *uuid.UUID `db:"warehouse_id"`
Slug string `db:"slug"`
}

25
internal/models/order.go Normal file
View File

@@ -0,0 +1,25 @@
package models
import (
"time"
"github.com/google/uuid"
)
type Order struct {
ID uuid.UUID `db:"id"`
CreatedByID *uuid.UUID `db:"created_by_id"`
DriverID *uuid.UUID `db:"driver_id"`
ManagerID *uuid.UUID `db:"manager_id"`
OriginWarehouseID *uuid.UUID `db:"origin_warehouse_id"`
OriginAddress string `db:"origin_address"`
DestinationAddress string `db:"destination_address"`
CargoDescription string `db:"cargo_description"`
WeightKg float64 `db:"weight_kg"`
VolumeM3 float64 `db:"volume_m3"`
Status string `db:"status"` // pending, assigned, in_transit, delivered, cancelled
TotalPrice float64 `db:"total_price"`
CreatedAt time.Time `db:"created_at"`
AssignedAt *time.Time `db:"assigned_at"`
DeliveredAt *time.Time `db:"delivered_at"`
}

33
internal/models/route.go Normal file
View File

@@ -0,0 +1,33 @@
package models
import (
"encoding/json"
"time"
"github.com/google/uuid"
)
// Coordinate represents a [longitude, latitude] pair — ORS array format.
type Coordinate [2]float64
type Route struct {
ID uuid.UUID `db:"id"`
OrderID uuid.UUID `db:"order_id"`
DriverID *uuid.UUID `db:"driver_id"`
Coordinates json.RawMessage `db:"coordinates"` // JSONB: [][2]float64
CurrentIndex int `db:"current_index"`
StartedAt *time.Time `db:"started_at"`
FinishedAt *time.Time `db:"finished_at"`
DistanceKm float64 `db:"distance_km"`
DurationSec int `db:"duration_sec"`
Status string `db:"status"` // pending, active, finished
}
// ParseCoordinates unmarshals the raw JSONB coordinates into a typed slice.
func (r *Route) ParseCoordinates() ([]Coordinate, error) {
var coords []Coordinate
if err := json.Unmarshal(r.Coordinates, &coords); err != nil {
return nil, err
}
return coords, nil
}

View File

@@ -0,0 +1,15 @@
package models
import "github.com/google/uuid"
type Vehicle struct {
ID uuid.UUID `db:"id"`
PlateNumber string `db:"plate_number"`
Brand string `db:"brand"`
Model string `db:"model"`
Year int `db:"year"`
CapacityKg float64 `db:"capacity_kg"`
CapacityM3 float64 `db:"capacity_m3"`
Status string `db:"status"` // available, in_transit, maintenance
Slug string `db:"slug"`
}

View File

@@ -0,0 +1,19 @@
package models
import (
"time"
"github.com/google/uuid"
)
type Warehouse struct {
ID uuid.UUID `db:"id"`
Slug string `db:"slug"`
Name string `db:"name"`
Address string `db:"address"`
City string `db:"city"`
Latitude float64 `db:"latitude"`
Longitude float64 `db:"longitude"`
Status string `db:"status"` // active, inactive
CreatedAt time.Time `db:"created_at"`
}

View File

@@ -12,6 +12,7 @@ CREATE TABLE IF NOT EXISTS users (
updated_at TIMESTAMPTZ,
last_login_at TIMESTAMPTZ
);
-- +goose StatementEnd
-- +goose Down

View File

@@ -1,9 +1,25 @@
-- +goose Up
-- +goose StatementBegin
SELECT 'up SQL query';
CREATE TABLE IF NOT EXISTS orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_by_id UUID REFERENCES users(id) ON DELETE SET NULL,
driver_id UUID REFERENCES drivers(id) ON DELETE SET NULL,
manager_id UUID REFERENCES managers(id) ON DELETE SET NULL,
origin_warehouse_id UUID REFERENCES warehouses(id) ON DELETE SET NULL,
origin_address VARCHAR(255),
destination_address VARCHAR(255) NOT NULL,
cargo_description TEXT,
weight_kg NUMERIC(10,2),
volume_m3 NUMERIC(10,2),
status VARCHAR(20) DEFAULT 'pending',
total_price NUMERIC(12,2),
created_at TIMESTAMPTZ DEFAULT NOW(),
assigned_at TIMESTAMPTZ,
delivered_at TIMESTAMPTZ
);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
SELECT 'down SQL query';
DROP TABLE IF EXISTS orders;
-- +goose StatementEnd

View File

@@ -0,0 +1,14 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE IF NOT EXISTS managers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID UNIQUE REFERENCES users(id) ON DELETE CASCADE,
warehouse_id UUID REFERENCES warehouses(id) ON DELETE SET NULL,
slug VARCHAR(120) UNIQUE NOT NULL
);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS managers;
-- +goose StatementEnd

View File

@@ -0,0 +1,19 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE IF NOT EXISTS warehouses (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
slug VARCHAR(120) UNIQUE NOT NULL,
name VARCHAR(150) NOT NULL,
address VARCHAR(255) NOT NULL,
city VARCHAR(100),
latitude NUMERIC(9,6),
longitude NUMERIC(9,6),
status VARCHAR(20) DEFAULT 'active',
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS warehouses;
-- +goose StatementEnd

View File

@@ -0,0 +1,20 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE IF NOT EXISTS routes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
order_id UUID UNIQUE REFERENCES orders(id) ON DELETE CASCADE,
driver_id UUID REFERENCES drivers(id) ON DELETE SET NULL,
coordinates JSONB,
current_index INTEGER DEFAULT 0,
started_at TIMESTAMPTZ,
finished_at TIMESTAMPTZ,
distance_km NUMERIC(10,2),
duration_sec INTEGER,
status VARCHAR(20) DEFAULT 'pending'
);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS routes;
-- +goose StatementEnd