355 lines
12 KiB
Go
355 lines
12 KiB
Go
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)
|
|
}
|
|
}
|
|
|
|
func GetProductPerformanceSalesBreakdownHandler(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), 45*time.Second)
|
|
defer cancel()
|
|
|
|
rows, err := queries.ListProductPerformanceSalesBreakdown(
|
|
ctx,
|
|
pg,
|
|
strings.TrimSpace(r.URL.Query().Get("mode")),
|
|
intQuery(r, "limit", 500),
|
|
)
|
|
if err != nil {
|
|
http.Error(w, "satis kpi kirilimi alinamadi: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
_ = json.NewEncoder(w).Encode(rows)
|
|
}
|
|
}
|
|
|
|
func GetProductPerformanceGroupedHandler(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()
|
|
|
|
mode := strings.TrimSpace(r.URL.Query().Get("mode"))
|
|
groupLevels := splitCSVQuery(r.URL.Query().Get("group_levels"))
|
|
limit := intQuery(r, "limit", 50000)
|
|
expanded := map[string]bool{}
|
|
if r.Method == http.MethodPost {
|
|
var body struct {
|
|
Mode string `json:"mode"`
|
|
GroupLevels []string `json:"group_levels"`
|
|
ExpandedKeys []string `json:"expanded_keys"`
|
|
Limit int `json:"limit"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
|
http.Error(w, "gecersiz grup istegi: "+err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
if strings.TrimSpace(body.Mode) != "" {
|
|
mode = strings.TrimSpace(body.Mode)
|
|
}
|
|
if len(body.GroupLevels) > 0 {
|
|
groupLevels = body.GroupLevels
|
|
}
|
|
if body.Limit > 0 {
|
|
limit = body.Limit
|
|
}
|
|
for _, key := range body.ExpandedKeys {
|
|
key = strings.TrimSpace(key)
|
|
if key != "" {
|
|
expanded[key] = true
|
|
}
|
|
}
|
|
} else {
|
|
for _, key := range strings.Split(r.URL.Query().Get("expanded_keys"), ",") {
|
|
key = strings.TrimSpace(key)
|
|
if key != "" {
|
|
expanded[key] = true
|
|
}
|
|
}
|
|
}
|
|
|
|
rows, err := queries.ListProductPerformanceGrouped(ctx, pg, queries.ProductPerformanceGroupedRequest{
|
|
Mode: mode,
|
|
GroupLevels: groupLevels,
|
|
ExpandedKeys: expanded,
|
|
Limit: limit,
|
|
})
|
|
if err != nil {
|
|
http.Error(w, "urun performans grup verisi alinamadi: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
_ = json.NewEncoder(w).Encode(rows)
|
|
}
|
|
}
|
|
|
|
func splitCSVQuery(value string) []string {
|
|
parts := strings.Split(value, ",")
|
|
out := make([]string, 0, len(parts))
|
|
for _, part := range parts {
|
|
part = strings.TrimSpace(part)
|
|
if part != "" {
|
|
out = append(out, part)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
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"
|
|
}
|