package processor import ( "context" "fmt" "time" "apskel-pos-be/internal/entities" "apskel-pos-be/internal/mappers" "apskel-pos-be/internal/models" "github.com/google/uuid" ) type UserDeviceRepository interface { Create(ctx context.Context, device *entities.UserDevice) error GetByID(ctx context.Context, id uuid.UUID) (*entities.UserDevice, error) GetByDeviceID(ctx context.Context, deviceID string, userID uuid.UUID) (*entities.UserDevice, error) GetByUserID(ctx context.Context, userID uuid.UUID) ([]*entities.UserDevice, error) Update(ctx context.Context, device *entities.UserDevice) error Delete(ctx context.Context, id uuid.UUID) error DeleteByUserID(ctx context.Context, userID uuid.UUID) error List(ctx context.Context, filters map[string]interface{}, limit, offset int) ([]*entities.UserDevice, int64, error) } type UserDeviceProcessor interface { RegisterDevice(ctx context.Context, req *models.RegisterUserDeviceRequest) (*models.UserDeviceResponse, error) UpdateDevice(ctx context.Context, id uuid.UUID, req *models.UpdateUserDeviceRequest) (*models.UserDeviceResponse, error) DeleteDevice(ctx context.Context, id uuid.UUID) error GetDeviceByID(ctx context.Context, id uuid.UUID) (*models.UserDeviceResponse, error) GetDevicesByUserID(ctx context.Context, userID uuid.UUID) ([]*models.UserDeviceResponse, error) ListDevices(ctx context.Context, filters map[string]interface{}, page, limit int) ([]*models.UserDeviceResponse, int, error) } type UserDeviceProcessorImpl struct { userDeviceRepo UserDeviceRepository } func NewUserDeviceProcessorImpl(userDeviceRepo UserDeviceRepository) *UserDeviceProcessorImpl { return &UserDeviceProcessorImpl{ userDeviceRepo: userDeviceRepo, } } func (p *UserDeviceProcessorImpl) RegisterDevice(ctx context.Context, req *models.RegisterUserDeviceRequest) (*models.UserDeviceResponse, error) { // Upsert: if device already registered for this user, update it existing, err := p.userDeviceRepo.GetByDeviceID(ctx, req.DeviceID, req.UserID) if err == nil && existing != nil { existing.DeviceName = req.DeviceName existing.DeviceType = req.DeviceType existing.Platform = req.Platform existing.FCMToken = req.FCMToken existing.AppVersion = req.AppVersion existing.OsVersion = req.OsVersion existing.IPAddress = req.IPAddress now := time.Now() existing.LastActiveAt = &now if err := p.userDeviceRepo.Update(ctx, existing); err != nil { return nil, fmt.Errorf("failed to update device: %w", err) } return mappers.UserDeviceEntityToResponse(existing), nil } deviceEntity := &entities.UserDevice{ UserID: req.UserID, DeviceID: req.DeviceID, DeviceName: req.DeviceName, DeviceType: req.DeviceType, Platform: req.Platform, FCMToken: req.FCMToken, AppVersion: req.AppVersion, OsVersion: req.OsVersion, IPAddress: req.IPAddress, } now := time.Now() deviceEntity.LastActiveAt = &now if err := p.userDeviceRepo.Create(ctx, deviceEntity); err != nil { return nil, fmt.Errorf("failed to register device: %w", err) } return mappers.UserDeviceEntityToResponse(deviceEntity), nil } func (p *UserDeviceProcessorImpl) UpdateDevice(ctx context.Context, id uuid.UUID, req *models.UpdateUserDeviceRequest) (*models.UserDeviceResponse, error) { deviceEntity, err := p.userDeviceRepo.GetByID(ctx, id) if err != nil { return nil, fmt.Errorf("device not found: %w", err) } if req.DeviceName != "" { deviceEntity.DeviceName = req.DeviceName } if req.DeviceType != "" { deviceEntity.DeviceType = req.DeviceType } if req.Platform != "" { deviceEntity.Platform = req.Platform } if req.FCMToken != "" { deviceEntity.FCMToken = req.FCMToken } if req.AppVersion != "" { deviceEntity.AppVersion = req.AppVersion } if req.OsVersion != "" { deviceEntity.OsVersion = req.OsVersion } now := time.Now() deviceEntity.LastActiveAt = &now if err := p.userDeviceRepo.Update(ctx, deviceEntity); err != nil { return nil, fmt.Errorf("failed to update device: %w", err) } return mappers.UserDeviceEntityToResponse(deviceEntity), nil } func (p *UserDeviceProcessorImpl) DeleteDevice(ctx context.Context, id uuid.UUID) error { _, err := p.userDeviceRepo.GetByID(ctx, id) if err != nil { return fmt.Errorf("device not found: %w", err) } if err := p.userDeviceRepo.Delete(ctx, id); err != nil { return fmt.Errorf("failed to delete device: %w", err) } return nil } func (p *UserDeviceProcessorImpl) GetDeviceByID(ctx context.Context, id uuid.UUID) (*models.UserDeviceResponse, error) { deviceEntity, err := p.userDeviceRepo.GetByID(ctx, id) if err != nil { return nil, fmt.Errorf("device not found: %w", err) } return mappers.UserDeviceEntityToResponse(deviceEntity), nil } func (p *UserDeviceProcessorImpl) GetDevicesByUserID(ctx context.Context, userID uuid.UUID) ([]*models.UserDeviceResponse, error) { deviceEntities, err := p.userDeviceRepo.GetByUserID(ctx, userID) if err != nil { return nil, fmt.Errorf("failed to get devices: %w", err) } return mappers.UserDeviceEntitiesToResponses(deviceEntities), nil } func (p *UserDeviceProcessorImpl) ListDevices(ctx context.Context, filters map[string]interface{}, page, limit int) ([]*models.UserDeviceResponse, int, error) { offset := (page - 1) * limit deviceEntities, total, err := p.userDeviceRepo.List(ctx, filters, limit, offset) if err != nil { return nil, 0, fmt.Errorf("failed to list devices: %w", err) } deviceResponses := mappers.UserDeviceEntitiesToResponses(deviceEntities) totalPages := int((total + int64(limit) - 1) / int64(limit)) return deviceResponses, totalPages, nil }