package main import ( "database/sql" "encoding/json" "log" "net/http" "strconv" "github.com/gorilla/mux" _ "github.com/mattn/go-sqlite3" "github.com/rs/cors" ) type Product struct { ID int `json:"id"` Title string `json:"title"` Price float64 `json:"price"` Description string `json:"description"` ImageURL string `json:"image_url"` } type Review struct { ID int `json:"id"` ProductID int `json:"product_id"` Username string `json:"username"` Rating int `json:"rating"` Comment string `json:"comment"` CreatedAt string `json:"createdAt"` } var db *sql.DB func main() { var err error db, err = sql.Open("sqlite3", "./products.db") if err != nil { log.Fatal("Ошибка открытия БД:", err) } defer db.Close() createTables() r := mux.NewRouter() // Маршруты для товаров r.HandleFunc("/product/{id}", getProductHandler).Methods("GET") // Маршруты для отзывов r.HandleFunc("/product/{product_id:[0-9]+}/reviews", getReviews).Methods("GET") r.HandleFunc("/product/{product_id:[0-9]+}/reviews", addReview).Methods("POST") corsHandler := cors.New(cors.Options{ AllowedOrigins: []string{"http://localhost:3000"}, AllowedMethods: []string{"GET", "POST"}, AllowedHeaders: []string{"Content-Type", "Authorization"}, AllowCredentials: true, }).Handler(r) log.Println("Сервер запущен на порту 8080...") if err := http.ListenAndServe(":8080", corsHandler); err != nil { log.Fatal("Ошибка запуска сервера:", err) } } func createTables() { // Создание таблицы товаров _, err := db.Exec(` CREATE TABLE IF NOT EXISTS products ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, price REAL NOT NULL, description TEXT, image_url TEXT ); `) if err != nil { log.Fatal("Ошибка создания таблицы products:", err) } // Создание таблицы отзывов _, err = db.Exec(` CREATE TABLE IF NOT EXISTS reviews ( id INTEGER PRIMARY KEY AUTOINCREMENT, product_id INTEGER NOT NULL, username TEXT NOT NULL, rating INTEGER NOT NULL CHECK(rating >= 1 AND rating <= 5), comment TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (product_id) REFERENCES products(id) ); `) if err != nil { log.Fatal("Ошибка создания таблицы reviews:", err) } } func getProductHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id, err := strconv.Atoi(vars["id"]) if err != nil { http.Error(w, "Некорректный ID", http.StatusBadRequest) return } var product Product err = db.QueryRow("SELECT id, title, price, description, image_url FROM products WHERE id = ?", id). Scan(&product.ID, &product.Title, &product.Price, &product.Description, &product.ImageURL) if err == sql.ErrNoRows { http.Error(w, "Товар не найден", http.StatusNotFound) return } else if err != nil { log.Println("Ошибка получения товара:", err) http.Error(w, "Ошибка сервера", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(product) } func addReview(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) productID, err := strconv.Atoi(vars["product_id"]) if err != nil { http.Error(w, "Некорректный product_id", http.StatusBadRequest) log.Println("Ошибка преобразования product_id:", err) return } var review Review if err := json.NewDecoder(r.Body).Decode(&review); err != nil { http.Error(w, "Некорректный JSON", http.StatusBadRequest) log.Println("Ошибка декодирования JSON:", err) return } // Проверяем существование товара var exists bool err = db.QueryRow("SELECT EXISTS(SELECT 1 FROM products WHERE id = ?)", productID).Scan(&exists) if err != nil { http.Error(w, "Ошибка проверки существования товара", http.StatusInternalServerError) return } if !exists { http.Error(w, "Товар не найден", http.StatusNotFound) return } // Устанавливаем product_id из URL review.ProductID = productID log.Printf("Добавление отзыва для товара %d: %+v", productID, review) stmt, err := db.Prepare(` INSERT INTO reviews (product_id, username, rating, comment, created_at) VALUES (?, ?, ?, ?, datetime('now')) `) if err != nil { http.Error(w, "Ошибка при подготовке SQL-запроса", http.StatusInternalServerError) log.Println("Ошибка при подготовке запроса:", err) return } defer stmt.Close() result, err := stmt.Exec(review.ProductID, review.Username, review.Rating, review.Comment) if err != nil { http.Error(w, "Ошибка при добавлении отзыва", http.StatusInternalServerError) log.Println("Ошибка при выполнении SQL-запроса:", err) return } id, _ := result.LastInsertId() review.ID = int(id) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(review) } func getReviews(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) productID := vars["product_id"] log.Printf("Получение отзывов для товара %s", productID) rows, err := db.Query(` SELECT id, product_id, username, rating, comment, created_at FROM reviews WHERE product_id = ? ORDER BY created_at DESC `, productID) if err != nil { http.Error(w, "Ошибка при получении отзывов", http.StatusInternalServerError) log.Println("Ошибка при запросе отзывов:", err) return } defer rows.Close() var reviews []Review for rows.Next() { var review Review if err := rows.Scan(&review.ID, &review.ProductID, &review.Username, &review.Rating, &review.Comment, &review.CreatedAt); err != nil { http.Error(w, "Ошибка при обработке данных", http.StatusInternalServerError) log.Println("Ошибка при сканировании строк:", err) return } reviews = append(reviews, review) } log.Printf("Найдено %d отзывов для товара %s", len(reviews), productID) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(reviews) }