- Go API server with PostgreSQL + Redis - AI floor replacement (OpenRouter Gemini) - Product database (10 brands, 3539 products) - Recommendation engine, calculator, articles - Redis async queue + worker pool - Hot product caching, brand view tracking - JWT auth, favorites, projects - Docker deployment ready Co-Authored-By: Claude <noreply@anthropic.com>
80 lines
2.2 KiB
Go
80 lines
2.2 KiB
Go
package handler
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
|
|
"floorvisualizer/internal/model"
|
|
"floorvisualizer/internal/repository"
|
|
"floorvisualizer/internal/service"
|
|
)
|
|
|
|
type RecommendResponse struct {
|
|
Source model.Product `json:"source"`
|
|
Products []model.Product `json:"products"`
|
|
Scores []string `json:"scores"`
|
|
Total int `json:"total"`
|
|
}
|
|
|
|
var productService *service.ProductService
|
|
|
|
func initProductService(db *sql.DB) {
|
|
if productService != nil { return }
|
|
products, err := repository.QueryAllProducts(db)
|
|
if err != nil {
|
|
log.Printf("Failed to load products: %v", err)
|
|
return
|
|
}
|
|
log.Printf("Loaded %d products for recommendation engine", len(products))
|
|
productService = service.NewProductService(products, model.DefaultEngineConfig())
|
|
}
|
|
|
|
func handleRecommend(db *sql.DB) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
sku := r.URL.Query().Get("sku")
|
|
if sku == "" {
|
|
writeJSON(w, 400, map[string]string{"error": "sku is required"})
|
|
return
|
|
}
|
|
|
|
initProductService(db)
|
|
if productService == nil {
|
|
writeJSON(w, 500, map[string]string{"error": "Engine not initialized"})
|
|
return
|
|
}
|
|
|
|
source, err := repository.GetProductBySKU(db, sku)
|
|
if err != nil {
|
|
writeJSON(w, 404, map[string]string{"error": "product not found"})
|
|
return
|
|
}
|
|
|
|
// Load variants & build specs for price-aware matching
|
|
if variants, _ := repository.GetVariantsByStyle(db, sku); len(variants) > 0 {
|
|
source.Variants = variants
|
|
allVariants := append([]model.Product{*source}, variants...)
|
|
for _, v := range allVariants {
|
|
source.Specs = append(source.Specs, model.ProductSpec{
|
|
SKU: v.SKU, SizeLabel: v.SizeLabel,
|
|
WidthIn: v.WidthIn, LengthIn: v.LengthIn, Finish: v.Finish,
|
|
PricePerSqft: v.PricePerSqft, PriceTier: v.PriceTier,
|
|
CoverageSqftPerBox: v.CoverageSqftPerBox, MainImageURL: v.MainImageURL,
|
|
})
|
|
}
|
|
}
|
|
|
|
results := productService.FindMatches(*source)
|
|
prods := make([]model.Product, len(results))
|
|
scores := make([]string, len(results))
|
|
for i, r := range results {
|
|
prods[i] = r.Product
|
|
scores[i] = fmt.Sprintf("%.0f%%", r.Score)
|
|
}
|
|
writeJSON(w, 200, RecommendResponse{
|
|
Source: *source, Products: prods, Scores: scores, Total: len(prods),
|
|
})
|
|
}
|
|
}
|