NAM-APJATEL-BACKEND/delivery/controller/devices_controller.go

394 lines
13 KiB
Go

package controller
import (
"encoding/json"
"fmt"
"mime/multipart"
"net/http"
"strconv"
"strings"
"users_management/m/config"
"users_management/m/middleware"
"users_management/m/model/dto/req"
"users_management/m/usecase"
"users_management/m/utils/common"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
type DeviceController struct {
du usecase.DeviceUseCase
rg *gin.RouterGroup
cfg *config.Config
}
func NewDeviceController(du usecase.DeviceUseCase, rg *gin.RouterGroup, cfg *config.Config) *DeviceController {
return &DeviceController{
du: du,
rg: rg,
cfg: cfg,
}
}
func (dc *DeviceController) Route() {
rg := dc.rg.Group("/devices")
rg.Use(middleware.ConditionalRequireAnyRole(dc.cfg,"Teknisi", "Admin", "Super Admin"))
{
rg.POST("", dc.CreateDevice())
rg.GET("", dc.GetAllDevices())
rg.GET("/:uuid", dc.GetDeviceByID())
rg.PUT("/:uuid", dc.UpdateDevice())
rg.POST("/bulk-upload-images", dc.BulkUploadImages())
}
}
func (dc *DeviceController) BulkUploadImages() gin.HandlerFunc {
return func(c *gin.Context) {
// Parse multipart form
err := c.Request.ParseMultipartForm(100 << 20) // 100MB for bulk upload
if err != nil {
common.ErrorResponses(c, http.StatusBadRequest, "Failed to parse multipart form")
return
}
// Get device data from form
deviceDataStr := c.PostForm("devices")
if deviceDataStr == "" {
common.ErrorResponses(c, http.StatusBadRequest, "devices data is required")
return
}
// Parse device data
var devicesData []req.BulkDeviceImageUploadDTO
if err := json.Unmarshal([]byte(deviceDataStr), &devicesData); err != nil {
common.ErrorResponses(c, http.StatusBadRequest, "Invalid devices data format")
return
}
// Get all image files
form := c.Request.MultipartForm
allFiles := form.File["images"]
if len(allFiles) == 0 {
common.ErrorResponses(c, http.StatusBadRequest, "No image files provided")
return
}
// Parse file distribution from form data
fileDistributionStr := c.PostForm("file_distribution")
var fileDistribution []int
if fileDistributionStr != "" {
if err := json.Unmarshal([]byte(fileDistributionStr), &fileDistribution); err != nil {
common.ErrorResponses(c, http.StatusBadRequest, "Invalid file_distribution format")
return
}
} else {
// Default: distribute files evenly
filesPerDevice := len(allFiles) / len(devicesData)
remainder := len(allFiles) % len(devicesData)
fileDistribution = make([]int, len(devicesData))
for i := range fileDistribution {
fileDistribution[i] = filesPerDevice
if i < remainder {
fileDistribution[i]++
}
}
}
// Validate file distribution
if len(fileDistribution) != len(devicesData) {
common.ErrorResponses(c, http.StatusBadRequest,
fmt.Sprintf("File distribution count (%d) must match device count (%d)",
len(fileDistribution), len(devicesData)))
return
}
totalExpectedFiles := 0
for _, count := range fileDistribution {
totalExpectedFiles += count
}
if totalExpectedFiles != len(allFiles) {
common.ErrorResponses(c, http.StatusBadRequest,
fmt.Sprintf("Total files (%d) must match file distribution sum (%d)",
len(allFiles), totalExpectedFiles))
return
}
// Call use case
err = dc.du.BulkUploadImagesMultiple(devicesData, allFiles, fileDistribution)
if err != nil {
common.ErrorResponses(c, http.StatusBadRequest, err.Error())
return
}
totalImages := len(allFiles)
common.SingleResponses(c, fmt.Sprintf("%d images uploaded successfully for %d devices", totalImages, len(devicesData)), nil)
}
}
func (dc *DeviceController) CreateDevice() gin.HandlerFunc {
return func(c *gin.Context) {
contentType := c.GetHeader("Content-Type")
// Handle JSON request (no images)
if strings.Contains(contentType, "application/json") {
var deviceDTO req.DeviceDTO
err := c.ShouldBindJSON(&deviceDTO)
if err != nil {
common.ErrorResponses(c, http.StatusBadRequest, err.Error())
return
}
err = dc.du.CreateDevice(deviceDTO)
if err != nil {
common.ErrorResponses(c, http.StatusBadRequest, err.Error())
return
}
common.SingleResponses(c, "Device has been created", nil)
return
}
// Parse multipart form
err := c.Request.ParseMultipartForm(50 << 20) // 50MB for multiple images
if err != nil {
common.ErrorResponses(c, http.StatusBadRequest, "Failed to parse multipart form")
return
}
// Extract form data for device
deviceCode := c.PostForm("device_code")
deviceType := c.PostForm("device_type")
longitudeStr := c.PostForm("longitude")
latitudeStr := c.PostForm("latitude")
portAmountStr := c.PostForm("port_amount")
status := c.PostForm("status")
province := c.PostForm("province")
city := c.PostForm("city")
district := c.PostForm("district")
// Validate required fields
if deviceCode == "" || deviceType == "" || longitudeStr == "" || latitudeStr == "" || status == "" {
common.ErrorResponses(c, http.StatusBadRequest, "Missing required fields")
return
}
// Parse coordinates
longitude, err := strconv.ParseFloat(longitudeStr, 64)
if err != nil {
common.ErrorResponses(c, http.StatusBadRequest, "Invalid longitude")
return
}
latitude, err := strconv.ParseFloat(latitudeStr, 64)
if err != nil {
common.ErrorResponses(c, http.StatusBadRequest, "Invalid latitude")
return
}
// Parse port amount
portAmount := 0
if portAmountStr != "" {
portAmount, err = strconv.Atoi(portAmountStr)
if err != nil {
common.ErrorResponses(c, http.StatusBadRequest, "Invalid port amount")
return
}
}
// Create DTO
deviceDTO := req.DeviceDTO{
DeviceCode: deviceCode,
DeviceType: deviceType,
Longitude: longitude,
Latitude: latitude,
PortAmount: portAmount,
Status: status,
}
// Handle optional string fields
if province != "" {
deviceDTO.Province = &province
}
if city != "" {
deviceDTO.City = &city
}
if district != "" {
deviceDTO.District = &district
}
// Get multiple image files
form := c.Request.MultipartForm
imageFiles := form.File["images"] // Multiple images
// Also support single image upload for backward compatibility
if len(imageFiles) == 0 {
if singleImage, err := c.FormFile("image"); err == nil {
imageFiles = []*multipart.FileHeader{singleImage}
}
}
err = dc.du.CreateDeviceWithMultipleImages(deviceDTO, imageFiles)
if err != nil {
common.ErrorResponses(c, http.StatusBadRequest, err.Error())
return
}
common.SingleResponses(c, "Device has been created", nil)
}
}
func (dc *DeviceController) GetAllDevices() gin.HandlerFunc {
return func(c *gin.Context) {
deviceType := c.Query("type")
if deviceType != "" {
deviceResp, err := dc.du.GetByType(deviceType)
if err != nil {
common.ErrorResponses(c, http.StatusBadRequest, "Device type not found")
return
}
common.SingleResponses(c, "Success", deviceResp)
return
}
devices, err := dc.du.GetAllDevices()
if err != nil {
common.ErrorResponses(c, http.StatusBadRequest, "something went wrong")
return
}
common.SingleResponses(c, "Success", devices)
}
}
func (dc *DeviceController) GetDeviceByID() gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("uuid")
uuid, err := uuid.Parse(id)
if err != nil{
common.ErrorResponses(c, http.StatusBadGateway,"Invalid UUID")
return
}
device, err := dc.du.GetByID(uuid)
if err != nil {
common.ErrorResponses(c, http.StatusBadRequest, "Device not found")
return
}
common.SingleResponses(c, "Success", device)
}
}
func (dc *DeviceController) UpdateDevice() gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("uuid")
uuid, err := uuid.Parse(id)
if err != nil {
common.ErrorResponses(c, http.StatusBadRequest, "Invalid UUID")
return
}
contentType := c.GetHeader("Content-Type")
// Handle JSON request (no images)
if strings.Contains(contentType, "application/json") {
var deviceDTO req.UpdateDeviceDTO
err = c.ShouldBindJSON(&deviceDTO)
if err != nil {
common.ErrorResponses(c, http.StatusBadRequest, "Invalid request")
return
}
err = dc.du.UpdateDevice(uuid, deviceDTO)
if err != nil {
common.ErrorResponses(c, http.StatusBadRequest, "Device not found")
return
}
common.SingleResponses(c, "Device has been updated", nil)
return
}
// Handle multipart form request
err = c.Request.ParseMultipartForm(50 << 20) // 50MB for multiple images
if err != nil {
common.ErrorResponses(c, http.StatusBadRequest, "Failed to parse multipart form")
return
}
// Create update DTO from form data
deviceUpdateDTO := req.UpdateDeviceDTO{}
if deviceCode := c.PostForm("device_code"); deviceCode != "" {
deviceUpdateDTO.DeviceCode = &deviceCode
}
if deviceType := c.PostForm("device_type"); deviceType != "" {
deviceUpdateDTO.DeviceType = &deviceType
}
if longitudeStr := c.PostForm("longitude"); longitudeStr != "" {
if longitude, err := strconv.ParseFloat(longitudeStr, 64); err == nil {
deviceUpdateDTO.Longitude = &longitude
} else {
common.ErrorResponses(c, http.StatusBadRequest, "Invalid longitude format")
return
}
}
if latitudeStr := c.PostForm("latitude"); latitudeStr != "" {
if latitude, err := strconv.ParseFloat(latitudeStr, 64); err == nil {
deviceUpdateDTO.Latitude = &latitude
} else {
common.ErrorResponses(c, http.StatusBadRequest, "Invalid latitude format")
return
}
}
if portAmountStr := c.PostForm("port_amount"); portAmountStr != "" {
if portAmount, err := strconv.Atoi(portAmountStr); err == nil {
deviceUpdateDTO.PortAmount = &portAmount
} else {
common.ErrorResponses(c, http.StatusBadRequest, "Invalid port amount format")
return
}
}
if status := c.PostForm("status"); status != "" {
deviceUpdateDTO.Status = &status
}
if province := c.PostForm("province"); province != "" {
deviceUpdateDTO.Province = &province
}
if city := c.PostForm("city"); city != "" {
deviceUpdateDTO.City = &city
}
if district := c.PostForm("district"); district != "" {
deviceUpdateDTO.District = &district
}
// Get multiple image files
form := c.Request.MultipartForm
imageFiles := form.File["images"] // Support multiple images
// Also support single image upload for backward compatibility
if len(imageFiles) == 0 {
if singleImage, err := c.FormFile("image"); err == nil {
imageFiles = []*multipart.FileHeader{singleImage}
}
}
// Handle replace_images flag
replaceImages := c.PostForm("replace_images") == "true"
err = dc.du.UpdateDeviceWithMultipleImages(uuid, deviceUpdateDTO, imageFiles, replaceImages)
if err != nil {
common.ErrorResponses(c, http.StatusBadRequest, err.Error())
return
}
common.SingleResponses(c, "Device has been updated", nil)
}
}