package routes import ( "bssapp-backend/queries" "bssapp-backend/utils" "context" "database/sql" "encoding/json" "net/http" "strconv" "strings" "time" ) func GetProductPerformanceHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 60*time.Second) defer cancel() f := queries.ProductPerformanceFilters{ Search: strings.TrimSpace(r.URL.Query().Get("q")), ProductCode: strings.TrimSpace(r.URL.Query().Get("product_code")), MarketKey: strings.TrimSpace(r.URL.Query().Get("market_key")), Kategori: strings.TrimSpace(r.URL.Query().Get("kategori")), Seri: strings.TrimSpace(r.URL.Query().Get("seri")), Bucket: strings.TrimSpace(r.URL.Query().Get("bucket")), SortBy: strings.TrimSpace(r.URL.Query().Get("sort_by")), Descending: boolQuery(r, "descending", true), Limit: intQuery(r, "limit", 100), Page: intQuery(r, "page", 1), } rows, total, err := queries.ListProductPerformance(ctx, pg, f) if err != nil { http.Error(w, "urun performans listesi alinamadi: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("X-Total-Count", strconv.Itoa(total)) _ = json.NewEncoder(w).Encode(map[string]any{ "rows": rows, "total_count": total, "page": f.Page, "limit": f.Limit, }) } } func GetProductPerformanceSalesDetailsHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second) defer cancel() rows, err := queries.ListProductPerformanceSalesDetails( ctx, pg, strings.TrimSpace(r.URL.Query().Get("product_code")), strings.TrimSpace(r.URL.Query().Get("color_code")), strings.TrimSpace(r.URL.Query().Get("yaka_kodu")), intQuery(r, "limit", 100), ) if err != nil { http.Error(w, "urun satis detayi alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceStockSizesHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") ctx, cancel := context.WithTimeout(r.Context(), 60*time.Second) defer cancel() rows, err := queries.ListProductPerformanceStockSizes( ctx, strings.TrimSpace(r.URL.Query().Get("product_code")), strings.TrimSpace(r.URL.Query().Get("color_code")), strings.TrimSpace(r.URL.Query().Get("yaka_kodu")), ) if err != nil { http.Error(w, "urun beden stok detayi alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceSummaryHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 30*time.Second) defer cancel() summary, err := queries.GetProductPerformanceSummary(ctx, pg) if err != nil { http.Error(w, "urun performans ozeti alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(summary) } } func GetProductPerformanceGeneralHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 60*time.Second) defer cancel() rows, err := queries.ListProductPerformanceGeneral(ctx, pg, intQuery(r, "limit", 500)) if err != nil { http.Error(w, "genel urun performans KPI alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceOrdersHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 90*time.Second) defer cancel() rows, err := queries.ListProductPerformanceOrderAnalysis(ctx, pg, intQuery(r, "limit", 500)) if err != nil { http.Error(w, "siparis analiz verisi alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceOrderGroupsHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 90*time.Second) defer cancel() rows, err := queries.ListProductPerformanceOrderGroups( ctx, pg, strings.TrimSpace(r.URL.Query().Get("breakdown")), intQuery(r, "limit", 500), ) if err != nil { http.Error(w, "siparis grup analiz verisi alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceOrderProductCustomersHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 90*time.Second) defer cancel() rows, err := queries.ListProductPerformanceOrderProductCustomers(ctx, pg, intQuery(r, "limit", 500)) if err != nil { http.Error(w, "urun musteri siparis analiz verisi alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceOrderMarketDetailsHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 90*time.Second) defer cancel() rows, err := queries.ListProductPerformanceOrderMarketDetails(ctx, pg, intQuery(r, "limit", 800)) if err != nil { http.Error(w, "piyasa siparis detay analiz verisi alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceMarketsHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 30*time.Second) defer cancel() rows, err := queries.ListProductPerformanceMarkets(ctx, pg, intQuery(r, "limit", 100)) if err != nil { http.Error(w, "piyasa performans ozeti alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceCountriesHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 30*time.Second) defer cancel() rows, err := queries.ListProductPerformanceCountries(ctx, pg, intQuery(r, "limit", 100)) if err != nil { http.Error(w, "ulke performans ozeti alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } func GetProductPerformanceCustomersHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") traceID := utils.TraceIDFromRequest(r) ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 30*time.Second) defer cancel() rows, err := queries.ListProductPerformanceCustomers( ctx, pg, strings.TrimSpace(r.URL.Query().Get("breakdown")), intQuery(r, "limit", 100), ) if err != nil { http.Error(w, "musteri performans kirilimi alinamadi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(rows) } } type productPerformanceRefreshPayload struct { Mode string `json:"mode"` Stage string `json:"stage"` ResumeAfter int `json:"resume_after"` SkipDelete bool `json:"skip_delete"` ProductPrefix string `json:"product_prefix"` StartDate string `json:"start_date"` EndDate string `json:"end_date"` } func PostProductPerformanceRefreshHandler(pg *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") var payload productPerformanceRefreshPayload if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { http.Error(w, "invalid payload", http.StatusBadRequest) return } now := time.Now() startDate := parseDateOrZero(payload.StartDate) endDate := parseDateOrZero(payload.EndDate) if endDate.IsZero() { endDate = now } ctx, cancel := context.WithTimeout(r.Context(), 4*time.Hour) defer cancel() result, err := queries.RefreshProductPerformance(ctx, pg, queries.ProductPerformanceRefreshRequest{ Mode: payload.Mode, Stage: payload.Stage, ResumeAfter: payload.ResumeAfter, SkipDelete: payload.SkipDelete, ProductPrefix: payload.ProductPrefix, StartDate: startDate, EndDate: endDate, }) if err != nil { http.Error(w, "urun performans refresh hatasi: "+err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(result) } } func intQuery(r *http.Request, key string, fallback int) int { raw := strings.TrimSpace(r.URL.Query().Get(key)) if raw == "" { return fallback } n, err := strconv.Atoi(raw) if err != nil || n <= 0 { return fallback } return n } func boolQuery(r *http.Request, key string, fallback bool) bool { raw := strings.TrimSpace(strings.ToLower(r.URL.Query().Get(key))) if raw == "" { return fallback } return raw == "1" || raw == "true" || raw == "yes" || raw == "on" } func parseDateOrZero(raw string) time.Time { raw = strings.TrimSpace(raw) if raw == "" { return time.Time{} } t, err := time.Parse("2006-01-02", raw) if err != nil { return time.Time{} } return t }