main.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. package main
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "log"
  9. "net/http"
  10. "os"
  11. "os/signal"
  12. "syscall"
  13. "time"
  14. "github.com/go-chi/chi"
  15. )
  16. const (
  17. dadataUrl = "https://dadata.ru/api/v2/"
  18. apiToken = "b7bbd64b04ac588df342f5a7df4aeb2d36c7e591"
  19. apiSecret = "68fa60140c8e34ffd6bac13f3e50db09df4da0b2"
  20. )
  21. // Интерфейс для клиента DaData
  22. type DaDataClientInterface interface {
  23. searchAddress(query string) ([]*Address, error)
  24. geocodeAddress(lat, lng string) ([]*Address, error)
  25. }
  26. // Структуры запросов и ответов
  27. type RequestAddressSearch struct {
  28. Query string `json:"query"`
  29. }
  30. type RequestAddressGeocode struct {
  31. Lat string `json:"lat"`
  32. Lng string `json:"lng"`
  33. }
  34. type ResponseAddress struct {
  35. Addresses []*Address `json:"addresses"`
  36. }
  37. type Address struct {
  38. Value string `json:"value"`
  39. City string `json:"city"`
  40. }
  41. type DaDataClient struct {
  42. ApiURL string
  43. ApiToken string
  44. Client *http.Client
  45. }
  46. func NewDaDataClient() *DaDataClient {
  47. return &DaDataClient{
  48. ApiURL: dadataUrl,
  49. ApiToken: apiToken,
  50. Client: &http.Client{},
  51. }
  52. }
  53. // Функция для отправки запросов к DaData
  54. func (c *DaDataClient) sendDaDataRequest(url string, body []byte) (*http.Response, error) {
  55. req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
  56. if err != nil {
  57. return nil, err
  58. }
  59. req.Header.Set("Content-Type", "application/json")
  60. req.Header.Set("Authorization", fmt.Sprintf("Token %s", c.ApiToken))
  61. return c.Client.Do(req)
  62. }
  63. // Реализация метода поиска адреса для реального клиента
  64. func (c *DaDataClient) searchAddress(query string) ([]*Address, error) {
  65. url := c.ApiURL + "suggest/address"
  66. body, err := json.Marshal(map[string]string{"query": query})
  67. if err != nil {
  68. return nil, err
  69. }
  70. resp, err := c.sendDaDataRequest(url, body)
  71. if err != nil {
  72. return nil, err
  73. }
  74. defer resp.Body.Close()
  75. if resp.StatusCode != http.StatusOK {
  76. return nil, errors.New("ошибка DaData")
  77. }
  78. var data struct {
  79. Suggestions []struct {
  80. Value string `json:"value"`
  81. Data struct {
  82. City string `json:"city"`
  83. } `json:"data"`
  84. } `json:"suggestions"`
  85. }
  86. err = json.NewDecoder(resp.Body).Decode(&data)
  87. if err != nil {
  88. return nil, err
  89. }
  90. var addresses []*Address
  91. for _, suggestion := range data.Suggestions {
  92. addresses = append(addresses, &Address{
  93. Value: suggestion.Value,
  94. City: suggestion.Data.City,
  95. })
  96. }
  97. return addresses, nil
  98. }
  99. // Реализация метода геокодирования для реального клиента
  100. func (c *DaDataClient) geocodeAddress(lat, lng string) ([]*Address, error) {
  101. url := c.ApiURL + "geolocate/address"
  102. body, err := json.Marshal(map[string]string{"lat": lat, "lon": lng})
  103. if err != nil {
  104. return nil, err
  105. }
  106. resp, err := c.sendDaDataRequest(url, body)
  107. if err != nil {
  108. return nil, err
  109. }
  110. defer resp.Body.Close()
  111. if resp.StatusCode != http.StatusOK {
  112. return nil, errors.New("ошибка DaData")
  113. }
  114. var data struct {
  115. Suggestions []struct {
  116. Value string `json:"value"`
  117. Data struct {
  118. City string `json:"city"`
  119. } `json:"data"`
  120. } `json:"suggestions"`
  121. }
  122. err = json.NewDecoder(resp.Body).Decode(&data)
  123. if err != nil {
  124. return nil, err
  125. }
  126. var addresses []*Address
  127. for _, suggestion := range data.Suggestions {
  128. addresses = append(addresses, &Address{
  129. Value: suggestion.Value,
  130. City: suggestion.Data.City,
  131. })
  132. }
  133. return addresses, nil
  134. }
  135. // Обработчик для поиска по адресу
  136. func handleAddressSearch(client DaDataClientInterface) http.HandlerFunc {
  137. return func(w http.ResponseWriter, r *http.Request) {
  138. var req RequestAddressSearch
  139. err := json.NewDecoder(r.Body).Decode(&req)
  140. if err != nil || req.Query == "" {
  141. http.Error(w, "Неверный формат запроса", http.StatusBadRequest)
  142. return
  143. }
  144. // Запрос к DaData
  145. addresses, err := client.searchAddress(req.Query)
  146. if err != nil {
  147. http.Error(w, "Ошибка сервиса DaData", http.StatusInternalServerError)
  148. return
  149. }
  150. resp := ResponseAddress{
  151. Addresses: addresses,
  152. }
  153. w.Header().Set("Content-Type", "application/json")
  154. json.NewEncoder(w).Encode(resp)
  155. }
  156. }
  157. // Обработчик для геокодирования
  158. func handleAddressGeocode(client DaDataClientInterface) http.HandlerFunc {
  159. return func(w http.ResponseWriter, r *http.Request) {
  160. var req RequestAddressGeocode
  161. err := json.NewDecoder(r.Body).Decode(&req)
  162. if err != nil || req.Lat == "" || req.Lng == "" {
  163. http.Error(w, "Неверный формат запроса", http.StatusBadRequest)
  164. return
  165. }
  166. // Запрос к DaData для геокодирования
  167. addresses, err := client.geocodeAddress(req.Lat, req.Lng)
  168. if err != nil {
  169. http.Error(w, "Ошибка сервиса DaData", http.StatusInternalServerError)
  170. return
  171. }
  172. resp := ResponseAddress{
  173. Addresses: addresses,
  174. }
  175. w.Header().Set("Content-Type", "application/json")
  176. json.NewEncoder(w).Encode(resp)
  177. }
  178. }
  179. func main() {
  180. daDataClient := NewDaDataClient()
  181. router := chi.NewRouter()
  182. router.Post("/api/address/search", handleAddressSearch(daDataClient))
  183. router.Post("/api/address/geocode", handleAddressGeocode(daDataClient))
  184. port := ":8080"
  185. server := &http.Server{
  186. Addr: port,
  187. Handler: router,
  188. ReadTimeout: 10 * time.Second,
  189. WriteTimeout: 10 * time.Second,
  190. }
  191. go func() {
  192. log.Printf("Запуск сервера на порту %s", port)
  193. if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
  194. log.Fatalf("Server error: %v", err)
  195. }
  196. }()
  197. sigChannel := make(chan os.Signal, 1)
  198. signal.Notify(sigChannel, syscall.SIGINT, syscall.SIGTERM)
  199. <-sigChannel
  200. log.Println("Пни меня нежно")
  201. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  202. defer cancel()
  203. if err := server.Shutdown(ctx); err != nil {
  204. log.Fatalf("Server shutdown error: %v", err)
  205. }
  206. log.Println("Server stopped gracefully")
  207. }