Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -555,6 +555,17 @@ func UserCreateRoute(db *sql.DB) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
if err := ensureOrderPriceListUserPriceGroupSchema(db); err != nil {
|
||||
log.Printf("USER CREATE PRICE GROUP SCHEMA ERROR user_id=%d err=%v", newID, err)
|
||||
http.Error(w, "Fiyat grubu tablosu hazirlanamadi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err := saveOrderPriceListUserPriceGroupsTx(tx, newID, payload.OrderPriceListPriceGroups); err != nil {
|
||||
log.Printf("USER CREATE PRICE GROUP SAVE ERROR user_id=%d err=%v", newID, err)
|
||||
http.Error(w, "Fiyat gruplari eklenemedi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
if pe, ok := err.(*pq.Error); ok {
|
||||
log.Printf(
|
||||
|
||||
278
svc/routes/order_price_list_user_price_groups.go
Normal file
278
svc/routes/order_price_list_user_price_groups.go
Normal file
@@ -0,0 +1,278 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"bssapp-backend/auth"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
type orderPriceListPriceGroupOption struct {
|
||||
Value string `json:"value"`
|
||||
Label string `json:"label"`
|
||||
}
|
||||
|
||||
type orderPriceListUserPriceGroupRow struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
Username string `json:"username"`
|
||||
FullName string `json:"full_name"`
|
||||
Email string `json:"email"`
|
||||
PriceGroups []string `json:"price_groups"`
|
||||
}
|
||||
|
||||
type orderPriceListUserPriceGroupPayload struct {
|
||||
PriceGroups []string `json:"price_groups"`
|
||||
}
|
||||
|
||||
var orderPriceListAllPriceGroups = []orderPriceListPriceGroupOption{
|
||||
{Value: "usd1", Label: "USD 1"},
|
||||
{Value: "usd2", Label: "USD 2"},
|
||||
{Value: "usd3", Label: "USD 3"},
|
||||
{Value: "usd4", Label: "USD 4"},
|
||||
{Value: "usd5", Label: "USD 5"},
|
||||
{Value: "usd6", Label: "USD 6"},
|
||||
{Value: "eur1", Label: "EUR 1"},
|
||||
{Value: "eur2", Label: "EUR 2"},
|
||||
{Value: "eur3", Label: "EUR 3"},
|
||||
{Value: "eur4", Label: "EUR 4"},
|
||||
{Value: "eur5", Label: "EUR 5"},
|
||||
{Value: "eur6", Label: "EUR 6"},
|
||||
{Value: "try1", Label: "TRY 1"},
|
||||
{Value: "try2", Label: "TRY 2"},
|
||||
{Value: "try3", Label: "TRY 3"},
|
||||
{Value: "try4", Label: "TRY 4"},
|
||||
{Value: "try5", Label: "TRY 5"},
|
||||
{Value: "try6", Label: "TRY 6"},
|
||||
}
|
||||
|
||||
func ensureOrderPriceListUserPriceGroupSchema(db *sql.DB) error {
|
||||
stmts := []string{
|
||||
`CREATE TABLE IF NOT EXISTS mk_order_price_list_user_price_group (
|
||||
user_id BIGINT NOT NULL,
|
||||
price_group TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
PRIMARY KEY (user_id, price_group)
|
||||
)`,
|
||||
`CREATE INDEX IF NOT EXISTS ix_order_price_list_user_price_group_user ON mk_order_price_list_user_price_group (user_id)`,
|
||||
`ALTER TABLE mk_order_price_list_user_price_group DROP CONSTRAINT IF EXISTS ck_order_price_list_user_price_group`,
|
||||
`ALTER TABLE mk_order_price_list_user_price_group ADD CONSTRAINT ck_order_price_list_user_price_group CHECK (price_group IN ('usd1','usd2','usd3','usd4','usd5','usd6','eur1','eur2','eur3','eur4','eur5','eur6','try1','try2','try3','try4','try5','try6'))`,
|
||||
}
|
||||
for _, stmt := range stmts {
|
||||
if _, err := db.Exec(stmt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func normalizeOrderPriceListPriceGroups(groups []string) []string {
|
||||
allowed := map[string]bool{}
|
||||
order := []string{}
|
||||
for _, opt := range orderPriceListAllPriceGroups {
|
||||
allowed[opt.Value] = true
|
||||
order = append(order, opt.Value)
|
||||
}
|
||||
set := map[string]bool{}
|
||||
for _, item := range groups {
|
||||
v := strings.ToLower(strings.TrimSpace(item))
|
||||
if allowed[v] {
|
||||
set[v] = true
|
||||
}
|
||||
}
|
||||
out := make([]string, 0, len(set))
|
||||
for _, v := range order {
|
||||
if set[v] {
|
||||
out = append(out, v)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func loadOrderPriceListUserPriceGroups(db *sql.DB, userID int64) ([]string, error) {
|
||||
if err := ensureOrderPriceListUserPriceGroupSchema(db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows, err := db.Query(`
|
||||
SELECT price_group
|
||||
FROM mk_order_price_list_user_price_group
|
||||
WHERE user_id = $1
|
||||
ORDER BY
|
||||
CASE SUBSTRING(price_group, 1, 3)
|
||||
WHEN 'usd' THEN 1
|
||||
WHEN 'eur' THEN 2
|
||||
WHEN 'try' THEN 3
|
||||
ELSE 9
|
||||
END,
|
||||
CAST(SUBSTRING(price_group, 4) AS INT)
|
||||
`, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var out []string
|
||||
for rows.Next() {
|
||||
var group string
|
||||
if err := rows.Scan(&group); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, strings.ToLower(strings.TrimSpace(group)))
|
||||
}
|
||||
return normalizeOrderPriceListPriceGroups(out), rows.Err()
|
||||
}
|
||||
|
||||
func saveOrderPriceListUserPriceGroupsTx(tx *sql.Tx, userID int64, groups []string) error {
|
||||
if _, err := tx.Exec(`DELETE FROM mk_order_price_list_user_price_group WHERE user_id = $1`, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, group := range normalizeOrderPriceListPriceGroups(groups) {
|
||||
if _, err := tx.Exec(`
|
||||
INSERT INTO mk_order_price_list_user_price_group (user_id, price_group, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $3)
|
||||
ON CONFLICT (user_id, price_group)
|
||||
DO UPDATE SET updated_at = EXCLUDED.updated_at
|
||||
`, userID, group, time.Now()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetOrderPriceListPriceGroupLookupsHandler(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
if err := ensureOrderPriceListUserPriceGroupSchema(db); err != nil {
|
||||
log.Printf("[order-price-list-price-groups] schema error: %v", err)
|
||||
http.Error(w, "price group schema error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"price_groups": orderPriceListAllPriceGroups})
|
||||
}
|
||||
}
|
||||
|
||||
func GetMyOrderPriceListPriceGroupsHandler(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
claims, ok := auth.GetClaimsFromContext(r.Context())
|
||||
if !ok || claims == nil {
|
||||
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
groups, err := loadOrderPriceListUserPriceGroups(db, int64(claims.ID))
|
||||
if err != nil {
|
||||
log.Printf("[order-price-list-price-groups] my groups error user=%d err=%v", claims.ID, err)
|
||||
http.Error(w, "price groups lookup error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"price_groups": groups,
|
||||
"restricted": len(groups) > 0,
|
||||
"all_groups": orderPriceListAllPriceGroups,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func GetUserOrderPriceListPriceGroupsHandler(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
id, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64)
|
||||
if err != nil || id <= 0 {
|
||||
http.Error(w, "invalid user id", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
groups, err := loadOrderPriceListUserPriceGroups(db, id)
|
||||
if err != nil {
|
||||
log.Printf("[order-price-list-price-groups] user groups error user=%d err=%v", id, err)
|
||||
http.Error(w, "price groups lookup error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"price_groups": groups})
|
||||
}
|
||||
}
|
||||
|
||||
func SaveUserOrderPriceListPriceGroupsHandler(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
id, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64)
|
||||
if err != nil || id <= 0 {
|
||||
http.Error(w, "invalid user id", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
var payload orderPriceListUserPriceGroupPayload
|
||||
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||
http.Error(w, "invalid payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := ensureOrderPriceListUserPriceGroupSchema(db); err != nil {
|
||||
http.Error(w, "price group schema error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
http.Error(w, "transaction error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer tx.Rollback()
|
||||
if err := saveOrderPriceListUserPriceGroupsTx(tx, id, payload.PriceGroups); err != nil {
|
||||
log.Printf("[order-price-list-price-groups] save error user=%d err=%v", id, err)
|
||||
http.Error(w, "price groups save error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
http.Error(w, "commit error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"success": true})
|
||||
}
|
||||
}
|
||||
|
||||
func GetOrderPriceListUserPriceGroupRowsHandler(db *sql.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
if err := ensureOrderPriceListUserPriceGroupSchema(db); err != nil {
|
||||
http.Error(w, "price group schema error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
rows, err := db.Query(`
|
||||
SELECT u.id, u.username, COALESCE(u.full_name, ''), COALESCE(u.email, ''),
|
||||
COALESCE(array_agg(m.price_group ORDER BY
|
||||
CASE SUBSTRING(m.price_group, 1, 3)
|
||||
WHEN 'usd' THEN 1
|
||||
WHEN 'eur' THEN 2
|
||||
WHEN 'try' THEN 3
|
||||
ELSE 9
|
||||
END,
|
||||
CAST(SUBSTRING(m.price_group, 4) AS INT)
|
||||
) FILTER (WHERE m.price_group IS NOT NULL), ARRAY[]::text[]) AS price_groups
|
||||
FROM mk_dfusr u
|
||||
LEFT JOIN mk_order_price_list_user_price_group m ON m.user_id = u.id
|
||||
WHERE COALESCE(u.is_active, TRUE) = TRUE
|
||||
GROUP BY u.id, u.username, u.full_name, u.email
|
||||
ORDER BY u.username
|
||||
`)
|
||||
if err != nil {
|
||||
log.Printf("[order-price-list-price-groups] rows error: %v", err)
|
||||
http.Error(w, "price groups rows error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
out := []orderPriceListUserPriceGroupRow{}
|
||||
for rows.Next() {
|
||||
var row orderPriceListUserPriceGroupRow
|
||||
if err := rows.Scan(&row.UserID, &row.Username, &row.FullName, &row.Email, pq.Array(&row.PriceGroups)); err != nil {
|
||||
http.Error(w, "price groups scan error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
row.PriceGroups = normalizeOrderPriceListPriceGroups(row.PriceGroups)
|
||||
out = append(out, row)
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(out)
|
||||
}
|
||||
}
|
||||
@@ -184,6 +184,12 @@ func handleUserGet(db *sql.DB, w http.ResponseWriter, userID int64) {
|
||||
}
|
||||
}
|
||||
|
||||
if groups, err := loadOrderPriceListUserPriceGroups(db, userID); err == nil {
|
||||
u.OrderPriceListPriceGroups = groups
|
||||
} else {
|
||||
log.Printf("WARN [UserDetail] order price list price groups lookup failed user_id=%d err=%v", userID, err)
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// 🟢 RESPONSE
|
||||
// --------------------------------------------------
|
||||
@@ -326,6 +332,17 @@ func handleUserUpdate(db *sql.DB, w http.ResponseWriter, r *http.Request, userID
|
||||
}
|
||||
}
|
||||
|
||||
if err := ensureOrderPriceListUserPriceGroupSchema(db); err != nil {
|
||||
log.Printf("ERROR [UserDetail] price group schema failed user_id=%d err=%v", userID, err)
|
||||
http.Error(w, "Fiyat grubu tablosu hazirlanamadi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err := saveOrderPriceListUserPriceGroupsTx(tx, userID, payload.OrderPriceListPriceGroups); err != nil {
|
||||
log.Printf("ERROR [UserDetail] price groups save failed user_id=%d err=%v", userID, err)
|
||||
http.Error(w, "Fiyat gruplari guncellenemedi", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
log.Printf("❌ [UserDetail] commit failed user_id=%d err=%v", userID, err)
|
||||
http.Error(w, "Commit başarısız", http.StatusInternalServerError)
|
||||
@@ -384,6 +401,7 @@ func handleUserDelete(db *sql.DB, w http.ResponseWriter, r *http.Request, userID
|
||||
`DELETE FROM dfusr_dprt WHERE dfusr_id = $1`,
|
||||
`DELETE FROM dfusr_piyasa WHERE dfusr_id = $1`,
|
||||
`DELETE FROM dfusr_nebim_user WHERE dfusr_id = $1`,
|
||||
`DELETE FROM mk_order_price_list_user_price_group WHERE user_id = $1`,
|
||||
}
|
||||
|
||||
isUndefinedTable := func(err error) bool {
|
||||
|
||||
Reference in New Issue
Block a user