Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-04-16 17:46:39 +03:00
parent 307282928c
commit f9728b8a4c
13 changed files with 906 additions and 109 deletions

View File

@@ -847,6 +847,11 @@ func main() {
auditlog.Init(pgDB, 1000) auditlog.Init(pgDB, 1000)
log.Println("🕵️ AuditLog sistemi başlatıldı (buffer=1000)") log.Println("🕵️ AuditLog sistemi başlatıldı (buffer=1000)")
// -------------------------------------------------------
// 🚀 TRANSLATION QUERY PERFORMANCE INDEXES
// -------------------------------------------------------
routes.EnsureTranslationPerfIndexes(pgDB)
// ------------------------------------------------------- // -------------------------------------------------------
// ✉️ MAILER INIT // ✉️ MAILER INIT
// ------------------------------------------------------- // -------------------------------------------------------

View File

@@ -5,12 +5,20 @@ import (
"bssapp-backend/models" "bssapp-backend/models"
"context" "context"
"database/sql" "database/sql"
"fmt"
"strings" "strings"
"time" "time"
) )
func GetProductPricingList(ctx context.Context) ([]models.ProductPricing, error) { func GetProductPricingList(ctx context.Context, limit int, offset int) ([]models.ProductPricing, error) {
const query = ` if limit <= 0 {
limit = 500
}
if offset < 0 {
offset = 0
}
query := `
WITH base_products AS ( WITH base_products AS (
SELECT SELECT
LTRIM(RTRIM(ProductCode)) AS ProductCode, LTRIM(RTRIM(ProductCode)) AS ProductCode,
@@ -27,6 +35,13 @@ func GetProductPricingList(ctx context.Context) ([]models.ProductPricing, error)
AND IsBlocked = 0 AND IsBlocked = 0
AND LEN(LTRIM(RTRIM(ProductCode))) = 13 AND LEN(LTRIM(RTRIM(ProductCode))) = 13
), ),
paged_products AS (
SELECT
bp.ProductCode
FROM base_products bp
ORDER BY bp.ProductCode
OFFSET %d ROWS FETCH NEXT %d ROWS ONLY
),
latest_base_price AS ( latest_base_price AS (
SELECT SELECT
LTRIM(RTRIM(b.ItemCode)) AS ItemCode, LTRIM(RTRIM(b.ItemCode)) AS ItemCode,
@@ -42,8 +57,8 @@ func GetProductPricingList(ctx context.Context) ([]models.ProductPricing, error)
AND LTRIM(RTRIM(b.CurrencyCode)) = 'USD' AND LTRIM(RTRIM(b.CurrencyCode)) = 'USD'
AND EXISTS ( AND EXISTS (
SELECT 1 SELECT 1
FROM base_products bp FROM paged_products pp
WHERE bp.ProductCode = LTRIM(RTRIM(b.ItemCode)) WHERE pp.ProductCode = LTRIM(RTRIM(b.ItemCode))
) )
), ),
stock_entry_dates AS ( stock_entry_dates AS (
@@ -61,8 +76,8 @@ func GetProductPricingList(ctx context.Context) ([]models.ProductPricing, error)
) )
AND EXISTS ( AND EXISTS (
SELECT 1 SELECT 1
FROM base_products bp FROM paged_products pp
WHERE bp.ProductCode = LTRIM(RTRIM(s.ItemCode)) WHERE pp.ProductCode = LTRIM(RTRIM(s.ItemCode))
) )
GROUP BY LTRIM(RTRIM(s.ItemCode)) GROUP BY LTRIM(RTRIM(s.ItemCode))
), ),
@@ -75,8 +90,8 @@ func GetProductPricingList(ctx context.Context) ([]models.ProductPricing, error)
AND LEN(LTRIM(RTRIM(s.ItemCode))) = 13 AND LEN(LTRIM(RTRIM(s.ItemCode))) = 13
AND EXISTS ( AND EXISTS (
SELECT 1 SELECT 1
FROM base_products bp FROM paged_products pp
WHERE bp.ProductCode = LTRIM(RTRIM(s.ItemCode)) WHERE pp.ProductCode = LTRIM(RTRIM(s.ItemCode))
) )
GROUP BY LTRIM(RTRIM(s.ItemCode)) GROUP BY LTRIM(RTRIM(s.ItemCode))
), ),
@@ -89,8 +104,8 @@ func GetProductPricingList(ctx context.Context) ([]models.ProductPricing, error)
AND LEN(LTRIM(RTRIM(p.ItemCode))) = 13 AND LEN(LTRIM(RTRIM(p.ItemCode))) = 13
AND EXISTS ( AND EXISTS (
SELECT 1 SELECT 1
FROM base_products bp FROM paged_products pp
WHERE bp.ProductCode = LTRIM(RTRIM(p.ItemCode)) WHERE pp.ProductCode = LTRIM(RTRIM(p.ItemCode))
) )
GROUP BY LTRIM(RTRIM(p.ItemCode)) GROUP BY LTRIM(RTRIM(p.ItemCode))
), ),
@@ -103,8 +118,8 @@ func GetProductPricingList(ctx context.Context) ([]models.ProductPricing, error)
AND LEN(LTRIM(RTRIM(r.ItemCode))) = 13 AND LEN(LTRIM(RTRIM(r.ItemCode))) = 13
AND EXISTS ( AND EXISTS (
SELECT 1 SELECT 1
FROM base_products bp FROM paged_products pp
WHERE bp.ProductCode = LTRIM(RTRIM(r.ItemCode)) WHERE pp.ProductCode = LTRIM(RTRIM(r.ItemCode))
) )
GROUP BY LTRIM(RTRIM(r.ItemCode)) GROUP BY LTRIM(RTRIM(r.ItemCode))
), ),
@@ -117,29 +132,29 @@ func GetProductPricingList(ctx context.Context) ([]models.ProductPricing, error)
AND LEN(LTRIM(RTRIM(d.ItemCode))) = 13 AND LEN(LTRIM(RTRIM(d.ItemCode))) = 13
AND EXISTS ( AND EXISTS (
SELECT 1 SELECT 1
FROM base_products bp FROM paged_products pp
WHERE bp.ProductCode = LTRIM(RTRIM(d.ItemCode)) WHERE pp.ProductCode = LTRIM(RTRIM(d.ItemCode))
) )
GROUP BY LTRIM(RTRIM(d.ItemCode)) GROUP BY LTRIM(RTRIM(d.ItemCode))
), ),
stock_totals AS ( stock_totals AS (
SELECT SELECT
bp.ProductCode AS ItemCode, pp.ProductCode AS ItemCode,
CAST(ROUND( CAST(ROUND(
ISNULL(sb.InventoryQty1, 0) ISNULL(sb.InventoryQty1, 0)
- ISNULL(pb.PickingQty1, 0) - ISNULL(pb.PickingQty1, 0)
- ISNULL(rb.ReserveQty1, 0) - ISNULL(rb.ReserveQty1, 0)
- ISNULL(db.DispOrderQty1, 0) - ISNULL(db.DispOrderQty1, 0)
, 2) AS DECIMAL(18, 2)) AS StockQty , 2) AS DECIMAL(18, 2)) AS StockQty
FROM base_products bp FROM paged_products pp
LEFT JOIN stock_base sb LEFT JOIN stock_base sb
ON sb.ItemCode = bp.ProductCode ON sb.ItemCode = pp.ProductCode
LEFT JOIN pick_base pb LEFT JOIN pick_base pb
ON pb.ItemCode = bp.ProductCode ON pb.ItemCode = pp.ProductCode
LEFT JOIN reserve_base rb LEFT JOIN reserve_base rb
ON rb.ItemCode = bp.ProductCode ON rb.ItemCode = pp.ProductCode
LEFT JOIN disp_base db LEFT JOIN disp_base db
ON db.ItemCode = bp.ProductCode ON db.ItemCode = pp.ProductCode
) )
SELECT SELECT
bp.ProductCode AS ProductCode, bp.ProductCode AS ProductCode,
@@ -155,7 +170,9 @@ func GetProductPricingList(ctx context.Context) ([]models.ProductPricing, error)
bp.Icerik, bp.Icerik,
bp.Karisim, bp.Karisim,
bp.Marka bp.Marka
FROM base_products bp FROM paged_products pp
INNER JOIN base_products bp
ON bp.ProductCode = pp.ProductCode
LEFT JOIN latest_base_price lp LEFT JOIN latest_base_price lp
ON lp.ItemCode = bp.ProductCode ON lp.ItemCode = bp.ProductCode
AND lp.rn = 1 AND lp.rn = 1
@@ -165,6 +182,7 @@ func GetProductPricingList(ctx context.Context) ([]models.ProductPricing, error)
ON st.ItemCode = bp.ProductCode ON st.ItemCode = bp.ProductCode
ORDER BY bp.ProductCode; ORDER BY bp.ProductCode;
` `
query = fmt.Sprintf(query, offset, limit)
var ( var (
rows *sql.Rows rows *sql.Rows

View File

@@ -29,7 +29,20 @@ func GetProductPricingListHandler(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 180*time.Second) ctx, cancel := context.WithTimeout(r.Context(), 180*time.Second)
defer cancel() defer cancel()
rows, err := queries.GetProductPricingList(ctx) limit := 500
if raw := strings.TrimSpace(r.URL.Query().Get("limit")); raw != "" {
if parsed, err := strconv.Atoi(raw); err == nil && parsed > 0 && parsed <= 10000 {
limit = parsed
}
}
offset := 0
if raw := strings.TrimSpace(r.URL.Query().Get("offset")); raw != "" {
if parsed, err := strconv.Atoi(raw); err == nil && parsed >= 0 && parsed <= 1000000 {
offset = parsed
}
}
rows, err := queries.GetProductPricingList(ctx, limit+1, offset)
if err != nil { if err != nil {
if isPricingTimeoutLike(err, ctx.Err()) { if isPricingTimeoutLike(err, ctx.Err()) {
log.Printf( log.Printf(
@@ -54,16 +67,29 @@ func GetProductPricingListHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Urun fiyatlandirma listesi alinamadi: "+err.Error(), http.StatusInternalServerError) http.Error(w, "Urun fiyatlandirma listesi alinamadi: "+err.Error(), http.StatusInternalServerError)
return return
} }
hasMore := len(rows) > limit
if hasMore {
rows = rows[:limit]
}
log.Printf( log.Printf(
"[ProductPricing] trace=%s success user=%s id=%d count=%d duration_ms=%d", "[ProductPricing] trace=%s success user=%s id=%d limit=%d offset=%d count=%d has_more=%t duration_ms=%d",
traceID, traceID,
claims.Username, claims.Username,
claims.ID, claims.ID,
limit,
offset,
len(rows), len(rows),
hasMore,
time.Since(started).Milliseconds(), time.Since(started).Milliseconds(),
) )
w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("Content-Type", "application/json; charset=utf-8")
if hasMore {
w.Header().Set("X-Has-More", "true")
} else {
w.Header().Set("X-Has-More", "false")
}
_ = json.NewEncoder(w).Encode(rows) _ = json.NewEncoder(w).Encode(rows)
} }

View File

@@ -0,0 +1,41 @@
package routes
import (
"database/sql"
"log"
"strings"
)
// EnsureTranslationPerfIndexes creates helpful indexes for translation listing/search.
// It is safe to run on each startup; failures are logged and do not stop the service.
func EnsureTranslationPerfIndexes(db *sql.DB) {
if db == nil {
return
}
statements := []string{
`CREATE EXTENSION IF NOT EXISTS pg_trgm`,
`CREATE INDEX IF NOT EXISTS idx_mk_translator_t_key_lang ON mk_translator (t_key, lang_code)`,
`CREATE INDEX IF NOT EXISTS idx_mk_translator_status_lang_updated ON mk_translator (status, lang_code, updated_at DESC)`,
`CREATE INDEX IF NOT EXISTS idx_mk_translator_manual_status ON mk_translator (is_manual, status)`,
`CREATE INDEX IF NOT EXISTS idx_mk_translator_source_type_expr ON mk_translator ((COALESCE(NULLIF(provider_meta->>'source_type',''),'dummy')))`,
`CREATE INDEX IF NOT EXISTS idx_mk_translator_source_text_trgm ON mk_translator USING gin (source_text_tr gin_trgm_ops)`,
`CREATE INDEX IF NOT EXISTS idx_mk_translator_translated_text_trgm ON mk_translator USING gin (translated_text gin_trgm_ops)`,
}
for _, stmt := range statements {
if _, err := db.Exec(stmt); err != nil {
log.Printf("[TranslationPerf] index_setup_warn sql=%q err=%v", summarizeSQL(stmt), err)
continue
}
log.Printf("[TranslationPerf] index_ready sql=%q", summarizeSQL(stmt))
}
}
func summarizeSQL(sqlText string) string {
s := strings.TrimSpace(sqlText)