234 lines
6.8 KiB
Go
234 lines
6.8 KiB
Go
// internal/services/user_service.go
|
|
package services
|
|
|
|
import (
|
|
"errors"
|
|
"lost-and-found/internal/models"
|
|
"lost-and-found/internal/repositories"
|
|
"lost-and-found/internal/utils"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type UserService struct {
|
|
db *gorm.DB // ✅ Tambahkan ini
|
|
userRepo *repositories.UserRepository
|
|
roleRepo *repositories.RoleRepository
|
|
auditLogRepo *repositories.AuditLogRepository
|
|
}
|
|
|
|
func NewUserService(db *gorm.DB) *UserService {
|
|
return &UserService{
|
|
db: db, // ✅ Tambahkan ini
|
|
userRepo: repositories.NewUserRepository(db),
|
|
roleRepo: repositories.NewRoleRepository(db),
|
|
auditLogRepo: repositories.NewAuditLogRepository(db),
|
|
}
|
|
}
|
|
|
|
// UpdateProfileRequest represents profile update data
|
|
type UpdateProfileRequest struct {
|
|
Name string `json:"name"`
|
|
Phone string `json:"phone"`
|
|
NRP string `json:"nrp"`
|
|
}
|
|
|
|
// ChangePasswordRequest represents password change data
|
|
type ChangePasswordRequest struct {
|
|
OldPassword string `json:"old_password" binding:"required"`
|
|
NewPassword string `json:"new_password" binding:"required,min=6"`
|
|
}
|
|
|
|
// GetProfile gets user profile
|
|
func (s *UserService) GetProfile(userID uint) (*models.User, error) {
|
|
return s.userRepo.FindByID(userID)
|
|
}
|
|
|
|
// UpdateProfile - TANPA enkripsi
|
|
func (s *UserService) UpdateProfile(userID uint, req UpdateProfileRequest, ipAddress, userAgent string) (*models.User, error) {
|
|
user, err := s.userRepo.FindByID(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Update fields
|
|
if req.Name != "" {
|
|
user.Name = req.Name
|
|
}
|
|
|
|
// ✅ Update phone TANPA enkripsi
|
|
if req.Phone != "" {
|
|
user.Phone = req.Phone
|
|
}
|
|
|
|
// ✅ Update NRP TANPA enkripsi
|
|
if req.NRP != "" {
|
|
// Check if NRP already exists for another user
|
|
existingNRP, _ := s.userRepo.FindByNRP(req.NRP)
|
|
if existingNRP != nil && existingNRP.ID != userID {
|
|
return nil, errors.New("NRP already used by another user")
|
|
}
|
|
user.NRP = req.NRP
|
|
}
|
|
|
|
if err := s.userRepo.Update(user); err != nil {
|
|
return nil, errors.New("failed to update profile")
|
|
}
|
|
|
|
// Log audit
|
|
s.auditLogRepo.Log(&userID, models.ActionUpdate, models.EntityUser, &userID,
|
|
"Profile updated", ipAddress, userAgent)
|
|
|
|
return user, nil
|
|
}
|
|
|
|
// ChangePassword changes user password
|
|
func (s *UserService) ChangePassword(userID uint, req ChangePasswordRequest, ipAddress, userAgent string) error {
|
|
user, err := s.userRepo.FindByID(userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Verify old password
|
|
if !utils.CheckPasswordHash(req.OldPassword, user.Password) {
|
|
return errors.New("invalid old password")
|
|
}
|
|
|
|
// Hash new password
|
|
hashedPassword, err := utils.HashPassword(req.NewPassword)
|
|
if err != nil {
|
|
return errors.New("failed to hash password")
|
|
}
|
|
|
|
user.Password = hashedPassword
|
|
if err := s.userRepo.Update(user); err != nil {
|
|
return errors.New("failed to change password")
|
|
}
|
|
|
|
// Log audit
|
|
s.auditLogRepo.Log(&userID, models.ActionUpdate, models.EntityUser, &userID,
|
|
"Password changed", ipAddress, userAgent)
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetUserStats gets user statistics
|
|
func (s *UserService) GetUserStats(userID uint) (map[string]interface{}, error) {
|
|
stats := make(map[string]interface{})
|
|
|
|
// ✅ Count items reported by user
|
|
var itemCount int64
|
|
if err := s.db.Model(&models.Item{}). // ← Ganti dari s.userRepo.db
|
|
Where("reporter_id = ? AND deleted_at IS NULL", userID).
|
|
Count(&itemCount).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
stats["items_reported"] = itemCount
|
|
|
|
// ✅ Count lost items reported
|
|
var lostItemCount int64
|
|
if err := s.db.Model(&models.LostItem{}). // ← Ganti dari s.userRepo.db
|
|
Where("user_id = ? AND deleted_at IS NULL", userID).
|
|
Count(&lostItemCount).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
stats["lost_items_reported"] = lostItemCount
|
|
|
|
// ✅ Count claims made
|
|
var claimCount int64
|
|
if err := s.db.Model(&models.Claim{}). // ← Ganti dari s.userRepo.db
|
|
Where("user_id = ? AND deleted_at IS NULL", userID).
|
|
Count(&claimCount).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
stats["claims_made"] = claimCount
|
|
|
|
// ✅ Count approved claims
|
|
var approvedClaimCount int64
|
|
if err := s.db.Model(&models.Claim{}). // ← Ganti dari s.userRepo.db
|
|
Where("user_id = ? AND status = ? AND deleted_at IS NULL", userID, models.ClaimStatusApproved).
|
|
Count(&approvedClaimCount).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
stats["claims_approved"] = approvedClaimCount
|
|
|
|
return stats, nil
|
|
}
|
|
|
|
// UpdateUserRole updates user role (admin only)
|
|
func (s *UserService) UpdateUserRole(adminID, userID, roleID uint, ipAddress, userAgent string) error {
|
|
// Verify role exists
|
|
role, err := s.roleRepo.FindByID(roleID)
|
|
if err != nil {
|
|
return errors.New("invalid role")
|
|
}
|
|
|
|
// Update role
|
|
if err := s.userRepo.UpdateRole(userID, roleID); err != nil {
|
|
return errors.New("failed to update user role")
|
|
}
|
|
|
|
// Log audit
|
|
s.auditLogRepo.Log(&adminID, models.ActionUpdate, models.EntityUser, &userID,
|
|
"Role updated to: "+role.Name, ipAddress, userAgent)
|
|
|
|
return nil
|
|
}
|
|
|
|
// BlockUser blocks a user (admin only)
|
|
func (s *UserService) BlockUser(adminID, userID uint, ipAddress, userAgent string) error {
|
|
// Cannot block self
|
|
if adminID == userID {
|
|
return errors.New("cannot block yourself")
|
|
}
|
|
|
|
if err := s.userRepo.BlockUser(userID); err != nil {
|
|
return errors.New("failed to block user")
|
|
}
|
|
|
|
// Log audit
|
|
s.auditLogRepo.Log(&adminID, models.ActionBlock, models.EntityUser, &userID,
|
|
"User blocked", ipAddress, userAgent)
|
|
|
|
return nil
|
|
}
|
|
|
|
// UnblockUser unblocks a user (admin only)
|
|
func (s *UserService) UnblockUser(adminID, userID uint, ipAddress, userAgent string) error {
|
|
if err := s.userRepo.UnblockUser(userID); err != nil {
|
|
return errors.New("failed to unblock user")
|
|
}
|
|
|
|
// Log audit
|
|
s.auditLogRepo.Log(&adminID, models.ActionUnblock, models.EntityUser, &userID,
|
|
"User unblocked", ipAddress, userAgent)
|
|
|
|
return nil
|
|
}
|
|
|
|
// DeleteUser deletes a user (admin only)
|
|
func (s *UserService) DeleteUser(adminID, userID uint, ipAddress, userAgent string) error {
|
|
// Cannot delete self
|
|
if adminID == userID {
|
|
return errors.New("cannot delete yourself")
|
|
}
|
|
|
|
if err := s.userRepo.Delete(userID); err != nil {
|
|
return errors.New("failed to delete user")
|
|
}
|
|
|
|
// Log audit
|
|
s.auditLogRepo.Log(&adminID, models.ActionDelete, models.EntityUser, &userID,
|
|
"User deleted", ipAddress, userAgent)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *UserService) GetAllUsers(page, limit int) ([]models.User, int64, error) {
|
|
return s.userRepo.FindAll(page, limit)
|
|
}
|
|
|
|
// GetUserByID gets user by ID
|
|
func (s *UserService) GetUserByID(userID uint) (*models.User, error) {
|
|
return s.userRepo.FindByID(userID)
|
|
} |