123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- package main
- import (
- "bytes"
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "log"
- "net/http"
- "os"
- "os/signal"
- "syscall"
- "time"
- "github.com/go-chi/chi"
- )
- const (
- dadataUrl = "https://dadata.ru/api/v2/"
- apiToken = "b7bbd64b04ac588df342f5a7df4aeb2d36c7e591"
- apiSecret = "68fa60140c8e34ffd6bac13f3e50db09df4da0b2"
- )
- // Интерфейс для клиента DaData
- type DaDataClientInterface interface {
- searchAddress(query string) ([]*Address, error)
- geocodeAddress(lat, lng string) ([]*Address, error)
- }
- // Структуры запросов и ответов
- type RequestAddressSearch struct {
- Query string `json:"query"`
- }
- type RequestAddressGeocode struct {
- Lat string `json:"lat"`
- Lng string `json:"lng"`
- }
- type ResponseAddress struct {
- Addresses []*Address `json:"addresses"`
- }
- type Address struct {
- Value string `json:"value"`
- City string `json:"city"`
- }
- type DaDataClient struct {
- ApiURL string
- ApiToken string
- Client *http.Client
- }
- func NewDaDataClient() *DaDataClient {
- return &DaDataClient{
- ApiURL: dadataUrl,
- ApiToken: apiToken,
- Client: &http.Client{},
- }
- }
- // Функция для отправки запросов к DaData
- func (c *DaDataClient) sendDaDataRequest(url string, body []byte) (*http.Response, error) {
- req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
- if err != nil {
- return nil, err
- }
- req.Header.Set("Content-Type", "application/json")
- req.Header.Set("Authorization", fmt.Sprintf("Token %s", c.ApiToken))
- return c.Client.Do(req)
- }
- // Реализация метода поиска адреса для реального клиента
- func (c *DaDataClient) searchAddress(query string) ([]*Address, error) {
- url := c.ApiURL + "suggest/address"
- body, err := json.Marshal(map[string]string{"query": query})
- if err != nil {
- return nil, err
- }
- resp, err := c.sendDaDataRequest(url, body)
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- return nil, errors.New("ошибка DaData")
- }
- var data struct {
- Suggestions []struct {
- Value string `json:"value"`
- Data struct {
- City string `json:"city"`
- } `json:"data"`
- } `json:"suggestions"`
- }
- err = json.NewDecoder(resp.Body).Decode(&data)
- if err != nil {
- return nil, err
- }
- var addresses []*Address
- for _, suggestion := range data.Suggestions {
- addresses = append(addresses, &Address{
- Value: suggestion.Value,
- City: suggestion.Data.City,
- })
- }
- return addresses, nil
- }
- // Реализация метода геокодирования для реального клиента
- func (c *DaDataClient) geocodeAddress(lat, lng string) ([]*Address, error) {
- url := c.ApiURL + "geolocate/address"
- body, err := json.Marshal(map[string]string{"lat": lat, "lon": lng})
- if err != nil {
- return nil, err
- }
- resp, err := c.sendDaDataRequest(url, body)
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- return nil, errors.New("ошибка DaData")
- }
- var data struct {
- Suggestions []struct {
- Value string `json:"value"`
- Data struct {
- City string `json:"city"`
- } `json:"data"`
- } `json:"suggestions"`
- }
- err = json.NewDecoder(resp.Body).Decode(&data)
- if err != nil {
- return nil, err
- }
- var addresses []*Address
- for _, suggestion := range data.Suggestions {
- addresses = append(addresses, &Address{
- Value: suggestion.Value,
- City: suggestion.Data.City,
- })
- }
- return addresses, nil
- }
- // Обработчик для поиска по адресу
- func handleAddressSearch(client DaDataClientInterface) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- var req RequestAddressSearch
- err := json.NewDecoder(r.Body).Decode(&req)
- if err != nil || req.Query == "" {
- http.Error(w, "Неверный формат запроса", http.StatusBadRequest)
- return
- }
- // Запрос к DaData
- addresses, err := client.searchAddress(req.Query)
- if err != nil {
- http.Error(w, "Ошибка сервиса DaData", http.StatusInternalServerError)
- return
- }
- resp := ResponseAddress{
- Addresses: addresses,
- }
- w.Header().Set("Content-Type", "application/json")
- json.NewEncoder(w).Encode(resp)
- }
- }
- // Обработчик для геокодирования
- func handleAddressGeocode(client DaDataClientInterface) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- var req RequestAddressGeocode
- err := json.NewDecoder(r.Body).Decode(&req)
- if err != nil || req.Lat == "" || req.Lng == "" {
- http.Error(w, "Неверный формат запроса", http.StatusBadRequest)
- return
- }
- // Запрос к DaData для геокодирования
- addresses, err := client.geocodeAddress(req.Lat, req.Lng)
- if err != nil {
- http.Error(w, "Ошибка сервиса DaData", http.StatusInternalServerError)
- return
- }
- resp := ResponseAddress{
- Addresses: addresses,
- }
- w.Header().Set("Content-Type", "application/json")
- json.NewEncoder(w).Encode(resp)
- }
- }
- func main() {
- daDataClient := NewDaDataClient()
- router := chi.NewRouter()
- router.Post("/api/address/search", handleAddressSearch(daDataClient))
- router.Post("/api/address/geocode", handleAddressGeocode(daDataClient))
- port := ":8080"
- server := &http.Server{
- Addr: port,
- Handler: router,
- ReadTimeout: 10 * time.Second,
- WriteTimeout: 10 * time.Second,
- }
- go func() {
- log.Printf("Запуск сервера на порту %s", port)
- if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
- log.Fatalf("Server error: %v", err)
- }
- }()
- sigChannel := make(chan os.Signal, 1)
- signal.Notify(sigChannel, syscall.SIGINT, syscall.SIGTERM)
- <-sigChannel
- log.Println("Пни меня нежно")
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
- defer cancel()
- if err := server.Shutdown(ctx); err != nil {
- log.Fatalf("Server shutdown error: %v", err)
- }
- log.Println("Server stopped gracefully")
- }
|