chore: build
This commit is contained in:
@@ -650,3 +650,84 @@ func (d *Database) CanUserAccessCommand(
|
||||
|
||||
return exists, err
|
||||
}
|
||||
|
||||
// GetClientPointsAndRewards retourne les points cumulés et les récompenses réclamées pour un client.
|
||||
func (d *Database) GetClientPointsAndRewards(username string) (pointsExtra map[string]int, pointsRedeemed map[string]int, err error) {
|
||||
var row struct {
|
||||
PointsExtraJSON []byte `gorm:"column:points_extra"`
|
||||
PointsRedeemedJSON []byte `gorm:"column:points_redeemed"`
|
||||
}
|
||||
if err = d.GDB.Raw(`
|
||||
SELECT COALESCE(points_extra, '{}'::jsonb) as points_extra,
|
||||
COALESCE(points_redeemed, '{}'::jsonb) as points_redeemed
|
||||
FROM clients WHERE username = ?`, username).Scan(&row).Error; err != nil {
|
||||
return nil, nil, fmt.Errorf("erreur lecture points client: %w", err)
|
||||
}
|
||||
pointsExtra = map[string]int{}
|
||||
pointsRedeemed = map[string]int{}
|
||||
if len(row.PointsExtraJSON) > 0 {
|
||||
json.Unmarshal(row.PointsExtraJSON, &pointsExtra)
|
||||
}
|
||||
if len(row.PointsRedeemedJSON) > 0 {
|
||||
json.Unmarshal(row.PointsRedeemedJSON, &pointsRedeemed)
|
||||
}
|
||||
return pointsExtra, pointsRedeemed, nil
|
||||
}
|
||||
|
||||
// ClaimPoolReward réclame une récompense pour un pool donné si le client a assez de points.
|
||||
// Retourne le nombre de récompenses disponibles restantes après la réclamation.
|
||||
func (d *Database) ClaimPoolReward(username, poolKey string, threshold int) (remainingAvailable int, err error) {
|
||||
var points, redeemed int
|
||||
|
||||
err = d.GDB.Transaction(func(tx *gorm.DB) error {
|
||||
var row struct {
|
||||
Points int `gorm:"column:pts"`
|
||||
Redeemed int `gorm:"column:redeemed"`
|
||||
}
|
||||
if err := tx.Raw(`
|
||||
SELECT
|
||||
COALESCE((points_extra->>?)::int, 0) as pts,
|
||||
COALESCE((points_redeemed->>?)::int, 0) as redeemed
|
||||
FROM clients WHERE username = ? FOR UPDATE`,
|
||||
poolKey, poolKey, username).Scan(&row).Error; err != nil {
|
||||
return fmt.Errorf("erreur lecture: %w", err)
|
||||
}
|
||||
points = row.Points
|
||||
redeemed = row.Redeemed
|
||||
|
||||
earned := points / threshold
|
||||
available := earned - redeemed
|
||||
if available <= 0 {
|
||||
return fmt.Errorf("pas de récompense disponible pour ce pool")
|
||||
}
|
||||
|
||||
return tx.Exec(`
|
||||
UPDATE clients
|
||||
SET points_redeemed = jsonb_set(
|
||||
COALESCE(points_redeemed, '{}'::jsonb),
|
||||
ARRAY[?],
|
||||
to_jsonb(COALESCE((points_redeemed->>?)::int, 0) + 1)
|
||||
), updated_at = CURRENT_TIMESTAMP
|
||||
WHERE username = ?`,
|
||||
poolKey, poolKey, username).Error
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
earned := points / threshold
|
||||
remainingAvailable = earned - (redeemed + 1)
|
||||
return remainingAvailable, nil
|
||||
}
|
||||
|
||||
// ResetClientRedeemed remet à zéro les récompenses réclamées (admin).
|
||||
func (d *Database) ResetClientRedeemed(username, poolKey string) error {
|
||||
if poolKey != "" {
|
||||
return d.GDB.Exec(`
|
||||
UPDATE clients SET points_redeemed = points_redeemed - ?, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE username = ?`, poolKey, username).Error
|
||||
}
|
||||
return d.GDB.Exec(`
|
||||
UPDATE clients SET points_redeemed = '{}'::jsonb, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE username = ?`, username).Error
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"gestion/models"
|
||||
)
|
||||
|
||||
func (d *Database) AddContact(contact *models.Contact) error {
|
||||
err := d.GDB.Create(contact).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Database) GetContact(id uint) (*models.Contact, error) {
|
||||
var contact models.Contact
|
||||
if err := d.GDB.First(&contact, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &contact, nil
|
||||
}
|
||||
|
||||
func (d *Database) UpdateContact(contact *models.Contact) error {
|
||||
err := d.GDB.Save(contact).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Database) DeleteContact(id uint) error {
|
||||
err := d.GDB.Delete(&models.Contact{}, id).Error
|
||||
return err
|
||||
}
|
||||
@@ -236,11 +236,24 @@ func InitDB() *Database {
|
||||
log.Fatalf("❌ Erreur migration clients.points_extra: %v", err)
|
||||
}
|
||||
|
||||
// Migration: récompenses réclamées par pool (nb de fois que la récompense a été obtenue)
|
||||
if _, err = database.Exec(`ALTER TABLE clients ADD COLUMN IF NOT EXISTS points_redeemed JSONB NOT NULL DEFAULT '{}'::jsonb`); err != nil {
|
||||
log.Fatalf("❌ Erreur migration clients.points_redeemed: %v", err)
|
||||
}
|
||||
|
||||
// Migration: flag "à venir" sur les produits
|
||||
if _, err = database.Exec(`ALTER TABLE products ADD COLUMN IF NOT EXISTS coming_soon BOOLEAN NOT NULL DEFAULT FALSE`); err != nil {
|
||||
log.Fatalf("❌ Erreur migration products.coming_soon: %v", err)
|
||||
}
|
||||
|
||||
// Migration: table contacts (SAV Telegram)
|
||||
if _, err = database.Exec(`CREATE TABLE IF NOT EXISTS contacts (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL
|
||||
)`); err != nil {
|
||||
log.Fatalf("❌ Erreur migration contacts: %v", err)
|
||||
}
|
||||
|
||||
// Lancer le nettoyage périodique des tokens expirés
|
||||
go database.cleanExpiredTokensPeriodically()
|
||||
|
||||
@@ -469,6 +482,14 @@ func (db *Database) createTables() error {
|
||||
`CREATE INDEX IF NOT EXISTS idx_issues_command ON delivery_issues(command_id);`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_issues_status ON delivery_issues(status);`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_issues_reported_by ON delivery_issues(reported_by);`,
|
||||
|
||||
// ============================
|
||||
// TABLE contacts
|
||||
// ============================
|
||||
`CREATE TABLE IF NOT EXISTS contacts (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL
|
||||
);`,
|
||||
}
|
||||
|
||||
for _, query := range queries {
|
||||
|
||||
@@ -24,9 +24,13 @@ func (d *Database) NotifyClient(username string, commandID int, notifType, messa
|
||||
Redis.LPush(RedisCtx, notifKey, notifJSON)
|
||||
Redis.Expire(RedisCtx, notifKey, 7*24*time.Hour)
|
||||
|
||||
if services.TelegramBot != nil && services.TelegramBot.IsConfigured() && services.TelegramBot.IsNotificationsEnabled() {
|
||||
if services.LBTelegram != nil && services.LBTelegram.IsConfigured() && services.TelegramBot != nil && services.TelegramBot.IsNotificationsEnabled() {
|
||||
if chatID, ok, err := d.GetClientTelegramChatID(username); err == nil && ok {
|
||||
go services.TelegramBot.SendMessage(chatID, fmt.Sprintf("🔔 <b>Notification</b>\n\n%s", message))
|
||||
go func(id int64, msg string) {
|
||||
if err := services.LBTelegram.SendNotification(id, fmt.Sprintf("🔔 <b>Notification</b>\n\n%s", msg)); err != nil {
|
||||
log.Printf("⚠️ [LB] notification client échouée pour %s: %v", username, err)
|
||||
}
|
||||
}(chatID, message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,9 +54,13 @@ func (d *Database) NotifyLivreur(username string, commandID int, notifType, mess
|
||||
Redis.LPush(RedisCtx, notifKey, notifJSON)
|
||||
Redis.Expire(RedisCtx, notifKey, 7*24*time.Hour)
|
||||
|
||||
if services.TelegramBot != nil && services.TelegramBot.IsConfigured() && services.TelegramBot.IsNotificationsEnabled() {
|
||||
if services.LBTelegram != nil && services.LBTelegram.IsConfigured() && services.TelegramBot != nil && services.TelegramBot.IsNotificationsEnabled() {
|
||||
if chatID, ok, err := d.GetUserTelegramChatID(username); err == nil && ok {
|
||||
go services.TelegramBot.SendMessage(chatID, fmt.Sprintf("🔔 <b>Notification</b>\n\n%s", message))
|
||||
go func(id int64, msg string) {
|
||||
if err := services.LBTelegram.SendNotification(id, fmt.Sprintf("🔔 <b>Notification</b>\n\n%s", msg)); err != nil {
|
||||
log.Printf("⚠️ [LB] notification livreur échouée pour %s: %v", username, err)
|
||||
}
|
||||
}(chatID, message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,11 +95,15 @@ func (d *Database) NotifyAllAdminCabine(commandID int, clientUsername, deliveryA
|
||||
Redis.LPush(RedisCtx, notifKey, notifJSON)
|
||||
Redis.Expire(RedisCtx, notifKey, 7*24*time.Hour)
|
||||
|
||||
if services.TelegramBot != nil && services.TelegramBot.IsConfigured() && services.TelegramBot.IsNotificationsEnabled() {
|
||||
if services.LBTelegram != nil && services.LBTelegram.IsConfigured() && services.TelegramBot != nil && services.TelegramBot.IsNotificationsEnabled() {
|
||||
if chatID, ok, err := d.GetUserTelegramChatID(u.Username); err == nil && ok {
|
||||
capturedChatID := chatID
|
||||
capturedMsg := msg
|
||||
go services.TelegramBot.SendMessage(capturedChatID, fmt.Sprintf("🔔 <b>Nouvelle commande</b>\n\n%s", capturedMsg))
|
||||
go func(id int64, m string) {
|
||||
if err := services.LBTelegram.SendNotification(id, fmt.Sprintf("🔔 <b>Nouvelle commande</b>\n\n%s", m)); err != nil {
|
||||
log.Printf("⚠️ [LB] notification admin/cabine échouée: %v", err)
|
||||
}
|
||||
}(capturedChatID, capturedMsg)
|
||||
}
|
||||
}
|
||||
count++
|
||||
@@ -126,11 +138,15 @@ func (d *Database) NotifyAllAdminCabineAlert(alertID int, livreurUsername, alert
|
||||
Redis.LPush(RedisCtx, notifKey, notifJSON)
|
||||
Redis.Expire(RedisCtx, notifKey, 7*24*time.Hour)
|
||||
|
||||
if services.TelegramBot != nil && services.TelegramBot.IsConfigured() && services.TelegramBot.IsNotificationsEnabled() {
|
||||
if services.LBTelegram != nil && services.LBTelegram.IsConfigured() && services.TelegramBot != nil && services.TelegramBot.IsNotificationsEnabled() {
|
||||
if chatID, ok, err := d.GetUserTelegramChatID(u.Username); err == nil && ok {
|
||||
capturedChatID := chatID
|
||||
capturedBody := body
|
||||
go services.TelegramBot.SendMessage(capturedChatID, fmt.Sprintf("🚨 <b>Alerte livreur</b>\n\n%s", capturedBody))
|
||||
go func(id int64, m string) {
|
||||
if err := services.LBTelegram.SendNotification(id, fmt.Sprintf("🚨 <b>Alerte livreur</b>\n\n%s", m)); err != nil {
|
||||
log.Printf("⚠️ [LB] notification alerte échouée: %v", err)
|
||||
}
|
||||
}(capturedChatID, capturedBody)
|
||||
}
|
||||
}
|
||||
count++
|
||||
|
||||
@@ -66,7 +66,8 @@ func DefaultSettings() models.AppSettings {
|
||||
},
|
||||
},
|
||||
},
|
||||
ShopName: "Milieu-Nantais",
|
||||
ShopName: "Milieu-Nantais",
|
||||
ContactTelegram: "MLN44LA",
|
||||
DeliveryMode: models.DeliveryModeConfig{
|
||||
Mode: "single",
|
||||
CategoryRoutes: []models.CategoryRoute{},
|
||||
@@ -111,6 +112,11 @@ func (d *Database) GetSettings() (models.AppSettings, error) {
|
||||
if err := json.Unmarshal([]byte(row.Value), &pools); err == nil {
|
||||
settings.PointsPools = pools
|
||||
}
|
||||
case "points_reward":
|
||||
var reward models.PointsReward
|
||||
if err := json.Unmarshal([]byte(row.Value), &reward); err == nil {
|
||||
settings.PointsReward = &reward
|
||||
}
|
||||
case "referral_enabled":
|
||||
settings.ReferralEnabled = row.Value == "true"
|
||||
case "referral_amount":
|
||||
@@ -140,6 +146,8 @@ func (d *Database) GetSettings() (models.AppSettings, error) {
|
||||
if err := json.Unmarshal([]byte(row.Value), &zones); err == nil {
|
||||
settings.PostalZones = zones
|
||||
}
|
||||
case "contact_telegram":
|
||||
settings.ContactTelegram = row.Value
|
||||
case "telegram_bot_token":
|
||||
settings.TelegramBotToken = row.Value
|
||||
case "telegram_bot_username":
|
||||
@@ -186,6 +194,11 @@ func (d *Database) UpdateSettings(s models.AppSettings) error {
|
||||
return fmt.Errorf("erreur sérialisation pools: %w", err)
|
||||
}
|
||||
|
||||
rewardJSON, err := json.Marshal(s.PointsReward)
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur sérialisation points_reward: %w", err)
|
||||
}
|
||||
|
||||
if s.NowPaymentsCurrencies == nil {
|
||||
s.NowPaymentsCurrencies = []string{}
|
||||
}
|
||||
@@ -215,11 +228,15 @@ func (d *Database) UpdateSettings(s models.AppSettings) error {
|
||||
return fmt.Errorf("erreur sérialisation delivery_mode: %w", err)
|
||||
}
|
||||
|
||||
if s.ContactTelegram == "" {
|
||||
s.ContactTelegram = "MLN44LA"
|
||||
}
|
||||
pairs := [][2]string{
|
||||
{"penalties_enabled", boolStr(s.PenaltiesEnabled)},
|
||||
{"show_amende_score", boolStr(s.ShowAmendeScore)},
|
||||
{"points_enabled", boolStr(s.PointsEnabled)},
|
||||
{"points_pools", string(poolsJSON)},
|
||||
{"points_reward", string(rewardJSON)},
|
||||
{"referral_enabled", boolStr(s.ReferralEnabled)},
|
||||
{"referral_amount", strconv.FormatFloat(s.ReferralAmount, 'f', 2, 64)},
|
||||
{"crypto_payment_enabled", boolStr(s.CryptoPaymentEnabled)},
|
||||
@@ -235,6 +252,7 @@ func (d *Database) UpdateSettings(s models.AppSettings) error {
|
||||
{"telegram_2fa_enabled", boolStr(s.Telegram2FAEnabled)},
|
||||
{"delivery_mode", string(deliveryModeJSON)},
|
||||
{"shop_name", s.ShopName},
|
||||
{"contact_telegram", s.ContactTelegram},
|
||||
}
|
||||
|
||||
upsert := `INSERT INTO app_settings (key, value) VALUES (?, ?)
|
||||
|
||||
Reference in New Issue
Block a user