This commit is contained in:
Tomas Dvorak
2025-10-19 18:09:28 +02:00
parent abe127fb51
commit 9ccca365b3
40 changed files with 6885 additions and 20 deletions
@@ -0,0 +1,296 @@
package controllers
import (
"fmt"
"fotbal-club/pkg/logger"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// BatchOperationsController handles bulk operations
type BatchOperationsController struct {
DB *gorm.DB
}
// NewBatchOperationsController creates a new BatchOperationsController instance
func NewBatchOperationsController(db *gorm.DB) *BatchOperationsController {
return &BatchOperationsController{DB: db}
}
// BatchDeleteRequest represents a batch delete request
type BatchDeleteRequest struct {
IDs []uint `json:"ids" binding:"required,min=1"`
}
// BatchUpdateRequest represents a batch update request
type BatchUpdateRequest struct {
IDs []uint `json:"ids" binding:"required,min=1"`
Fields map[string]interface{} `json:"fields" binding:"required"`
}
// BatchResult represents the result of a batch operation
type BatchResult struct {
Success bool `json:"success"`
TotalItems int `json:"total_items"`
SuccessCount int `json:"success_count"`
FailureCount int `json:"failure_count"`
Errors []string `json:"errors,omitempty"`
}
// BatchDelete performs a batch delete operation
func (boc *BatchOperationsController) BatchDelete(c *gin.Context, model interface{}, tableName string) {
var req BatchDeleteRequest
if err := c.ShouldBindJSON(&req); err != nil {
Respond.BadRequest(c, "Invalid request: "+err.Error())
return
}
if len(req.IDs) == 0 {
Respond.BadRequest(c, "No IDs provided")
return
}
// Perform batch delete
result := boc.DB.Where("id IN ?", req.IDs).Delete(model)
if result.Error != nil {
logger.Error("Batch delete failed for %s: %v", tableName, result.Error)
Respond.InternalError(c, "Failed to perform batch delete")
return
}
batchResult := BatchResult{
Success: true,
TotalItems: len(req.IDs),
SuccessCount: int(result.RowsAffected),
FailureCount: len(req.IDs) - int(result.RowsAffected),
}
logger.Info("Batch deleted %d %s records", result.RowsAffected, tableName)
Respond.Success(c, batchResult, fmt.Sprintf("Successfully deleted %d items", result.RowsAffected))
}
// BatchUpdate performs a batch update operation
func (boc *BatchOperationsController) BatchUpdate(c *gin.Context, model interface{}, tableName string, allowedFields []string) {
var req BatchUpdateRequest
if err := c.ShouldBindJSON(&req); err != nil {
Respond.BadRequest(c, "Invalid request: "+err.Error())
return
}
if len(req.IDs) == 0 {
Respond.BadRequest(c, "No IDs provided")
return
}
if len(req.Fields) == 0 {
Respond.BadRequest(c, "No fields to update")
return
}
// Validate fields
updates := make(map[string]interface{})
for field, value := range req.Fields {
allowed := false
for _, af := range allowedFields {
if field == af {
allowed = true
break
}
}
if !allowed {
Respond.BadRequest(c, fmt.Sprintf("Field '%s' is not allowed for batch update", field))
return
}
updates[field] = value
}
// Perform batch update
result := boc.DB.Model(model).Where("id IN ?", req.IDs).Updates(updates)
if result.Error != nil {
logger.Error("Batch update failed for %s: %v", tableName, result.Error)
Respond.InternalError(c, "Failed to perform batch update")
return
}
batchResult := BatchResult{
Success: true,
TotalItems: len(req.IDs),
SuccessCount: int(result.RowsAffected),
FailureCount: len(req.IDs) - int(result.RowsAffected),
}
logger.Info("Batch updated %d %s records", result.RowsAffected, tableName)
Respond.Success(c, batchResult, fmt.Sprintf("Successfully updated %d items", result.RowsAffected))
}
// BatchPublish publishes/unpublishes multiple items (for publishable entities)
func (boc *BatchOperationsController) BatchPublish(c *gin.Context, model interface{}, tableName string, publish bool) {
var req BatchDeleteRequest
if err := c.ShouldBindJSON(&req); err != nil {
Respond.BadRequest(c, "Invalid request: "+err.Error())
return
}
if len(req.IDs) == 0 {
Respond.BadRequest(c, "No IDs provided")
return
}
// Update published status
result := boc.DB.Model(model).Where("id IN ?", req.IDs).Update("published", publish)
if result.Error != nil {
logger.Error("Batch publish failed for %s: %v", tableName, result.Error)
Respond.InternalError(c, "Failed to perform batch publish")
return
}
action := "published"
if !publish {
action = "unpublished"
}
batchResult := BatchResult{
Success: true,
TotalItems: len(req.IDs),
SuccessCount: int(result.RowsAffected),
FailureCount: len(req.IDs) - int(result.RowsAffected),
}
logger.Info("Batch %s %d %s records", action, result.RowsAffected, tableName)
Respond.Success(c, batchResult, fmt.Sprintf("Successfully %s %d items", action, result.RowsAffected))
}
// BatchArchive archives/unarchives multiple items (for archivable entities)
func (boc *BatchOperationsController) BatchArchive(c *gin.Context, model interface{}, tableName string, archive bool) {
var req BatchDeleteRequest
if err := c.ShouldBindJSON(&req); err != nil {
Respond.BadRequest(c, "Invalid request: "+err.Error())
return
}
if len(req.IDs) == 0 {
Respond.BadRequest(c, "No IDs provided")
return
}
// Update archived status
result := boc.DB.Model(model).Where("id IN ?", req.IDs).Update("archived", archive)
if result.Error != nil {
logger.Error("Batch archive failed for %s: %v", tableName, result.Error)
Respond.InternalError(c, "Failed to perform batch archive")
return
}
action := "archived"
if !archive {
action = "unarchived"
}
batchResult := BatchResult{
Success: true,
TotalItems: len(req.IDs),
SuccessCount: int(result.RowsAffected),
FailureCount: len(req.IDs) - int(result.RowsAffected),
}
logger.Info("Batch %s %d %s records", action, result.RowsAffected, tableName)
Respond.Success(c, batchResult, fmt.Sprintf("Successfully %s %d items", action, result.RowsAffected))
}
// BatchReorder reorders items based on provided order
func (boc *BatchOperationsController) BatchReorder(c *gin.Context, model interface{}, tableName string) {
var req struct {
Orders []struct {
ID uint `json:"id" binding:"required"`
Order int `json:"order" binding:"required"`
} `json:"orders" binding:"required,min=1"`
}
if err := c.ShouldBindJSON(&req); err != nil {
Respond.BadRequest(c, "Invalid request: "+err.Error())
return
}
// Update order for each item
successCount := 0
errors := []string{}
for _, item := range req.Orders {
result := boc.DB.Model(model).Where("id = ?", item.ID).Update("display_order", item.Order)
if result.Error != nil {
errors = append(errors, fmt.Sprintf("Failed to update order for ID %d: %v", item.ID, result.Error))
continue
}
successCount++
}
batchResult := BatchResult{
Success: len(errors) == 0,
TotalItems: len(req.Orders),
SuccessCount: successCount,
FailureCount: len(errors),
Errors: errors,
}
if len(errors) > 0 {
logger.Warn("Batch reorder partially failed for %s: %d successes, %d failures", tableName, successCount, len(errors))
Respond.Custom(c, 207, false, batchResult, "", "Batch reorder completed with errors") // 207 Multi-Status
return
}
logger.Info("Batch reordered %d %s records", successCount, tableName)
Respond.Success(c, batchResult, fmt.Sprintf("Successfully reordered %d items", successCount))
}
// BatchDuplicate duplicates multiple items
func (boc *BatchOperationsController) BatchDuplicate(c *gin.Context, tableName string, duplicateFunc func(id uint) error) {
var req BatchDeleteRequest
if err := c.ShouldBindJSON(&req); err != nil {
Respond.BadRequest(c, "Invalid request: "+err.Error())
return
}
if len(req.IDs) == 0 {
Respond.BadRequest(c, "No IDs provided")
return
}
// Duplicate each item
successCount := 0
errors := []string{}
for _, id := range req.IDs {
if err := duplicateFunc(id); err != nil {
errors = append(errors, fmt.Sprintf("Failed to duplicate ID %d: %v", id, err))
continue
}
successCount++
}
batchResult := BatchResult{
Success: len(errors) == 0,
TotalItems: len(req.IDs),
SuccessCount: successCount,
FailureCount: len(errors),
Errors: errors,
}
if len(errors) > 0 {
logger.Warn("Batch duplicate partially failed for %s: %d successes, %d failures", tableName, successCount, len(errors))
Respond.Custom(c, 207, false, batchResult, "", "Batch duplicate completed with errors")
return
}
logger.Info("Batch duplicated %d %s records", successCount, tableName)
Respond.Success(c, batchResult, fmt.Sprintf("Successfully duplicated %d items", successCount))
}
// Global BatchOperationsController instance (needs to be initialized with DB)
var BatchOps *BatchOperationsController
// InitBatchOperations initializes the global batch operations controller
func InitBatchOperations(db *gorm.DB) {
BatchOps = NewBatchOperationsController(db)
}