296 lines
9.6 KiB
Go
296 lines
9.6 KiB
Go
// internal/controllers/item_controller.go
|
|
package controllers
|
|
|
|
import (
|
|
"lost-and-found/internal/models"
|
|
"lost-and-found/internal/services"
|
|
"lost-and-found/internal/repositories" // ✅ TAMBAHKAN INI
|
|
"lost-and-found/internal/utils"
|
|
"net/http"
|
|
"strconv"
|
|
"log"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type ItemController struct {
|
|
itemService *services.ItemService
|
|
matchService *services.MatchService
|
|
itemRepo *repositories.ItemRepository // ✅ TAMBAHKAN INI
|
|
}
|
|
|
|
func NewItemController(db *gorm.DB) *ItemController {
|
|
return &ItemController{
|
|
itemService: services.NewItemService(db),
|
|
matchService: services.NewMatchService(db),
|
|
itemRepo: repositories.NewItemRepository(db), // ✅ INITIALIZE INI
|
|
}
|
|
}
|
|
|
|
// ✅ FIXED GetItemByID
|
|
// ✅ PASTIKAN response detail lengkap untuk manager
|
|
// ✅ FIXED GetItemByIDfunc (c *ItemController) GetItemByID(ctx *gin.Context) {
|
|
// ✅ FIXED GetItemByID - NOW RETURNS FULL DETAILS FOR MANAGER
|
|
func (c *ItemController) GetItemByID(ctx *gin.Context) {
|
|
id, err := strconv.ParseUint(ctx.Param("id"), 10, 32)
|
|
if err != nil {
|
|
utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid item ID", err.Error())
|
|
return
|
|
}
|
|
|
|
// ✅ Check if user is manager or admin
|
|
isManager := false
|
|
if userObj, exists := ctx.Get("user"); exists {
|
|
user := userObj.(*models.User)
|
|
isManager = user.IsManager() || user.IsAdmin()
|
|
}
|
|
|
|
// ✅ Get item DIRECTLY from repository with PRELOAD
|
|
item, err := c.itemRepo.FindByID(uint(id))
|
|
if err != nil {
|
|
utils.ErrorResponse(ctx, http.StatusNotFound, "Item not found", err.Error())
|
|
return
|
|
}
|
|
|
|
// ✅ LOG untuk debug
|
|
log.Printf("🔍 Controller GetItemByID - Item ID: %d", item.ID)
|
|
log.Printf(" 📝 Description: %s", item.Description)
|
|
log.Printf(" 🔒 SecretDetails: %s", item.SecretDetails)
|
|
log.Printf(" 👤 ReporterName: %s", item.ReporterName)
|
|
log.Printf(" 📞 ReporterContact: %s", item.ReporterContact)
|
|
|
|
// ✅ Return response based on role
|
|
if isManager {
|
|
// Manager/Admin gets FULL details
|
|
detailResponse := item.ToDetailResponse()
|
|
|
|
// ✅ LOG response yang akan dikirim
|
|
log.Printf("📤 Sending DetailResponse to Manager:")
|
|
log.Printf(" Description: %s", detailResponse.Description)
|
|
log.Printf(" SecretDetails: %s", detailResponse.SecretDetails)
|
|
log.Printf(" ReporterName: %s", detailResponse.ReporterName)
|
|
|
|
utils.SuccessResponse(ctx, http.StatusOK, "Item retrieved", detailResponse)
|
|
} else {
|
|
// Regular user gets public view only
|
|
utils.SuccessResponse(ctx, http.StatusOK, "Item retrieved", item.ToPublicResponse())
|
|
}
|
|
}
|
|
|
|
func (c *ItemController) ReportFoundItemLinked(ctx *gin.Context) {
|
|
userObj, _ := ctx.Get("user")
|
|
user := userObj.(*models.User)
|
|
|
|
var req services.CreateFoundItemLinkedRequest
|
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
|
utils.ErrorResponse(ctx, http.StatusBadRequest, "Data tidak valid", err.Error())
|
|
return
|
|
}
|
|
|
|
ipAddress := ctx.ClientIP()
|
|
userAgent := ctx.Request.UserAgent()
|
|
|
|
item, err := c.itemService.CreateFoundItemLinked(user.ID, req, ipAddress, userAgent)
|
|
if err != nil {
|
|
utils.ErrorResponse(ctx, http.StatusInternalServerError, "Gagal membuat laporan", err.Error())
|
|
return
|
|
}
|
|
|
|
message := "Laporan berhasil dibuat. Menunggu verifikasi Manager."
|
|
if req.IsDirectToOwner {
|
|
message = "Laporan berhasil! Notifikasi langsung dikirim ke pemilik barang."
|
|
}
|
|
|
|
utils.SuccessResponse(ctx, http.StatusCreated, message, item)
|
|
}
|
|
// CreateItem creates a new item
|
|
// POST /api/items
|
|
func (c *ItemController) CreateItem(ctx *gin.Context) {
|
|
userObj, _ := ctx.Get("user")
|
|
user := userObj.(*models.User)
|
|
|
|
var req services.CreateItemRequest
|
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
|
utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid request data", err.Error())
|
|
return
|
|
}
|
|
|
|
ipAddress := ctx.ClientIP()
|
|
userAgent := ctx.Request.UserAgent()
|
|
|
|
item, err := c.itemService.CreateItem(user.ID, req, ipAddress, userAgent)
|
|
if err != nil {
|
|
utils.ErrorResponse(ctx, http.StatusBadRequest, "Failed to create item", err.Error())
|
|
return
|
|
}
|
|
|
|
// Auto-match with lost items
|
|
go c.matchService.AutoMatchNewItem(item.ID)
|
|
|
|
utils.SuccessResponse(ctx, http.StatusCreated, "Item created", item.ToDetailResponse())
|
|
}
|
|
|
|
// UpdateItem updates an item
|
|
// PUT /api/items/:id
|
|
func (c *ItemController) UpdateItem(ctx *gin.Context) {
|
|
userObj, _ := ctx.Get("user")
|
|
user := userObj.(*models.User)
|
|
|
|
itemID, err := strconv.ParseUint(ctx.Param("id"), 10, 32)
|
|
if err != nil {
|
|
utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid item ID", err.Error())
|
|
return
|
|
}
|
|
|
|
var req services.UpdateItemRequest
|
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
|
utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid request data", err.Error())
|
|
return
|
|
}
|
|
|
|
ipAddress := ctx.ClientIP()
|
|
userAgent := ctx.Request.UserAgent()
|
|
|
|
item, err := c.itemService.UpdateItem(user.ID, uint(itemID), req, ipAddress, userAgent)
|
|
if err != nil {
|
|
utils.ErrorResponse(ctx, http.StatusBadRequest, "Failed to update item", err.Error())
|
|
return
|
|
}
|
|
|
|
utils.SuccessResponse(ctx, http.StatusOK, "Item updated", item.ToDetailResponse())
|
|
}
|
|
|
|
// UpdateItemStatus updates item status
|
|
// PATCH /api/items/:id/status
|
|
func (c *ItemController) UpdateItemStatus(ctx *gin.Context) {
|
|
userObj, _ := ctx.Get("user")
|
|
user := userObj.(*models.User)
|
|
|
|
itemID, err := strconv.ParseUint(ctx.Param("id"), 10, 32)
|
|
if err != nil {
|
|
utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid item ID", err.Error())
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Status string `json:"status" binding:"required"`
|
|
}
|
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
|
utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid request data", err.Error())
|
|
return
|
|
}
|
|
|
|
ipAddress := ctx.ClientIP()
|
|
userAgent := ctx.Request.UserAgent()
|
|
|
|
if err := c.itemService.UpdateItemStatus(user.ID, uint(itemID), req.Status, ipAddress, userAgent); err != nil {
|
|
utils.ErrorResponse(ctx, http.StatusBadRequest, "Failed to update status", err.Error())
|
|
return
|
|
}
|
|
|
|
utils.SuccessResponse(ctx, http.StatusOK, "Item status updated", nil)
|
|
}
|
|
|
|
// DeleteItem deletes an item
|
|
// DELETE /api/items/:id
|
|
func (c *ItemController) DeleteItem(ctx *gin.Context) {
|
|
userObj, _ := ctx.Get("user")
|
|
user := userObj.(*models.User)
|
|
|
|
itemID, err := strconv.ParseUint(ctx.Param("id"), 10, 32)
|
|
if err != nil {
|
|
utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid item ID", err.Error())
|
|
return
|
|
}
|
|
|
|
ipAddress := ctx.ClientIP()
|
|
userAgent := ctx.Request.UserAgent()
|
|
|
|
if err := c.itemService.DeleteItem(user.ID, uint(itemID), ipAddress, userAgent); err != nil {
|
|
utils.ErrorResponse(ctx, http.StatusBadRequest, "Failed to delete item", err.Error())
|
|
return
|
|
}
|
|
|
|
utils.SuccessResponse(ctx, http.StatusOK, "Item deleted", nil)
|
|
}
|
|
|
|
// GetItemsByReporter gets items by reporter
|
|
// GET /api/user/items
|
|
func (c *ItemController) GetItemsByReporter(ctx *gin.Context) {
|
|
userObj, _ := ctx.Get("user")
|
|
user := userObj.(*models.User)
|
|
|
|
page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1"))
|
|
limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10"))
|
|
|
|
items, total, err := c.itemService.GetItemsByReporter(user.ID, page, limit)
|
|
if err != nil {
|
|
utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to get items", err.Error())
|
|
return
|
|
}
|
|
|
|
var responses []models.ItemDetailResponse
|
|
for _, item := range items {
|
|
responses = append(responses, item.ToDetailResponse())
|
|
}
|
|
|
|
utils.SendPaginatedResponse(ctx, http.StatusOK, "Items retrieved", responses, total, page, limit)
|
|
}
|
|
|
|
// GetItemRevisionHistory gets revision history for an item
|
|
// GET /api/items/:id/revisions
|
|
func (c *ItemController) GetItemRevisionHistory(ctx *gin.Context) {
|
|
itemID, err := strconv.ParseUint(ctx.Param("id"), 10, 32)
|
|
if err != nil {
|
|
utils.ErrorResponse(ctx, http.StatusBadRequest, "Invalid item ID", err.Error())
|
|
return
|
|
}
|
|
|
|
page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1"))
|
|
limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10"))
|
|
|
|
revisions, total, err := c.itemService.GetItemRevisionHistory(uint(itemID), page, limit)
|
|
if err != nil {
|
|
utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to get revision history", err.Error())
|
|
return
|
|
}
|
|
|
|
utils.SendPaginatedResponse(ctx, http.StatusOK, "Revision history retrieved", revisions, total, page, limit)
|
|
}
|
|
|
|
func (c *ItemController) GetAllItems(ctx *gin.Context) {
|
|
page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1"))
|
|
limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10"))
|
|
status := ctx.Query("status")
|
|
category := ctx.Query("category")
|
|
search := ctx.Query("search")
|
|
|
|
// ✅ CHECK IF USER IS MANAGER/ADMIN
|
|
isManager := false
|
|
if userObj, exists := ctx.Get("user"); exists {
|
|
user := userObj.(*models.User)
|
|
isManager = user.IsManager() || user.IsAdmin()
|
|
}
|
|
|
|
// ✅ FIXED: FORCE FILTER OUT EXPIRED for public users
|
|
if !isManager {
|
|
// 1. Jika user mencoba meminta status terlarang, paksa filter aman
|
|
if status == models.ItemStatusExpired || status == models.ItemStatusCaseClosed {
|
|
status = "!expired"
|
|
}
|
|
|
|
// 2. Jika status kosong (default), set ke !expired
|
|
if status == "" {
|
|
status = "!expired"
|
|
}
|
|
}
|
|
|
|
items, total, err := c.itemService.GetAllItems(page, limit, status, category, search)
|
|
if err != nil {
|
|
utils.ErrorResponse(ctx, http.StatusInternalServerError, "Failed to get items", err.Error())
|
|
return
|
|
}
|
|
|
|
utils.SendPaginatedResponse(ctx, http.StatusOK, "Items retrieved", items, total, page, limit)
|
|
} |