394 lines
13 KiB
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)
|
|
}
|
|
} |