Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import org.springframework.stereotype.Repository;
import ua.everybuy.database.entity.Advertisement;
import ua.everybuy.routing.dto.CategoryAdvertisementCount;

import java.util.List;
import java.util.Optional;

Expand Down Expand Up @@ -52,5 +51,10 @@ List<CategoryAdvertisementCount> findCategoryCountsByUserIdAndSection(
WHERE a.id = :id AND a.isEnabled = true
""")
Optional<Advertisement> findActiveById(@Param("id") Long id);
}

@Query("SELECT a.city.id FROM Advertisement a " +
"WHERE a.userId = :userId " +
"ORDER BY a.updateDate DESC LIMIT 1")
Optional<Long> findFirstCityIdByUserId(@Param("userId") Long userId);

}
30 changes: 19 additions & 11 deletions src/main/java/ua/everybuy/routing/controller/UserController.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import ua.everybuy.service.advertisement.user.AdvertisementUserDeletionService;
import ua.everybuy.service.advertisement.user.AdvertisementUserProfileService;
import ua.everybuy.service.advertisement.user.AdvertisementUserStatisticService;
import ua.everybuy.database.entity.Advertisement;
import ua.everybuy.database.entity.City;
import ua.everybuy.routing.dto.UserAdvertisementDto;
import ua.everybuy.routing.dto.response.AdvertisementWithStatisticResponse;
import ua.everybuy.routing.dto.response.StatusResponse;

import ua.everybuy.service.advertisement.user.UserDeletionService;
import ua.everybuy.service.advertisement.user.UserLastLocationService;
import ua.everybuy.service.advertisement.user.UserProfileService;
import ua.everybuy.service.advertisement.user.UserStatisticService;
import java.io.IOException;
import java.security.Principal;
import java.util.List;
Expand All @@ -21,9 +22,10 @@
@RequestMapping("/product/user")
@RequiredArgsConstructor
public class UserController {
private final AdvertisementUserStatisticService advertisementUserStatisticService;
private final AdvertisementUserProfileService advertisementUserProfileService;
private final AdvertisementUserDeletionService advertisementUserDeletionService;
private final UserStatisticService userStatisticService;
private final UserProfileService userProfileService;
private final UserDeletionService userDeletionService;
private final UserLastLocationService userLastLocationService;

@GetMapping("/active-ads")
@ResponseStatus(HttpStatus.OK)
Expand All @@ -32,7 +34,7 @@ public StatusResponse<List<AdvertisementWithStatisticResponse>> getAllActiveUser
Principal principal, @RequestParam(required = false, defaultValue = "SELL") @Valid Advertisement.AdSection section,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "8") int size) {
List<AdvertisementWithStatisticResponse> responseList = advertisementUserStatisticService
List<AdvertisementWithStatisticResponse> responseList = userStatisticService
.getUserAdvertisementsWithStatistic(Long.parseLong(principal.getName()), true, section, page, size);
return new StatusResponse<>(HttpStatus.OK.value(), responseList);
}
Expand All @@ -43,7 +45,7 @@ public StatusResponse<List<AdvertisementWithStatisticResponse>> getAllNotActiveU
Principal principal, @RequestParam(required = false, defaultValue = "SELL") @Valid Advertisement.AdSection section,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "8") int size) {
List<AdvertisementWithStatisticResponse> responseList = advertisementUserStatisticService
List<AdvertisementWithStatisticResponse> responseList = userStatisticService
.getUserAdvertisementsWithStatistic(Long.parseLong(principal.getName()), false, section, page, size);
return new StatusResponse<>(HttpStatus.OK.value(), responseList);
}
Expand All @@ -56,13 +58,19 @@ public UserAdvertisementDto getUserAds(@PathVariable Long userId, @RequestParam(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "20") int size) {

return advertisementUserProfileService.getUserActiveFilteredAdvertisements(userId, categoryId, section, page, size);
return userProfileService.getUserActiveFilteredAdvertisements(userId, categoryId, section, page, size);
}

@DeleteMapping("/{userId}/ads")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteUserAdvertisements(@PathVariable Long userId,
HttpServletRequest request) throws IOException {
advertisementUserDeletionService.deleteAllAndPushUserAdvertisements(userId, request);
userDeletionService.deleteAllAndPushUserAdvertisements(userId, request);
}

@GetMapping("/last-location")
@ResponseStatus(HttpStatus.OK)
public City getUserLastLocation(Principal principal) {
return userLastLocationService.getLastLocationForUser(Long.parseLong(principal.getName()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ua.everybuy.service.advertisement.user;

import jakarta.servlet.http.HttpServletRequest;

import java.io.IOException;

/**
* Service handling complete advertisement cleanup when a user account is deleted.
* Performs cascading deletion of all user advertisement assets.
*/
public interface UserDeletionService {
void deleteAllAndPushUserAdvertisements(Long userId, HttpServletRequest request) throws IOException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ua.everybuy.service.advertisement.user;

import jakarta.persistence.EntityNotFoundException;
import ua.everybuy.database.entity.City;

public interface UserLastLocationService {
/**
* Retrieves the last city a user selected when creating an advertisement
* @param userId the ID of the user to check
* @return the City entity of the last used location
* @throws EntityNotFoundException if user has no advertisement history
*/
City getLastLocationForUser(Long userId);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package ua.everybuy.service.advertisement.user;

import jakarta.persistence.EntityNotFoundException;
import ua.everybuy.database.entity.Advertisement;
import ua.everybuy.routing.dto.UserAdvertisementDto;

/**
* Used when browsing seller profiles to display their active listings with filtering capabilities.
*/
public interface UserProfileService {
/**
* Retrieves paginated and filtered advertisements for a user's profile page
* @param userId ID of the user/seller to view
* @param categoryId Optional category filter (null for all categories)
* @param section Advertisement section (BUY/SELL)
* @param page Page number (1-based)
* @param size Number of items per page
* @return UserAdvertisementDto containing:
* - Filtered advertisements
* - Total advertisement count
* - Category statistics
* @throws EntityNotFoundException if user or category doesn't exist
*/
UserAdvertisementDto getUserActiveFilteredAdvertisements(Long userId, Long categoryId,
Advertisement.AdSection section,
int page, int size);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ua.everybuy.service.advertisement.user;

import ua.everybuy.database.entity.Advertisement;
import ua.everybuy.routing.dto.response.AdvertisementWithStatisticResponse;
import java.util.List;

/**
* Provides access to a user's own advertisements with detailed statistics.
* Used in personal account/profile sections to show performance metrics.
*/
public interface UserStatisticService {
List<AdvertisementWithStatisticResponse> getUserAdvertisementsWithStatistic(Long userId, boolean isEnabled,
Advertisement.AdSection section,
int page, int size);
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package ua.everybuy.service.advertisement.user;
package ua.everybuy.service.advertisement.user.impl;

import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ua.everybuy.service.advertisement.AdvertisementManagementService;
import ua.everybuy.service.advertisement.user.UserDeletionService;
import ua.everybuy.service.photo.PhotoService;
import ua.everybuy.database.entity.Advertisement;
import ua.everybuy.database.entity.AdvertisementPhoto;
Expand All @@ -15,13 +16,14 @@

@Service
@RequiredArgsConstructor
public class AdvertisementUserDeletionService {
public class AdvertisementUserDeletionService implements UserDeletionService {
private final AdvertisementRepository advertisementRepository;
private final AdvertisementManagementService advertisementManagementService;
private final PhotoService photoService;
private final SecurityValidationService securityValidationService;

@Transactional
@Override
public void deleteAllAndPushUserAdvertisements(Long userId, HttpServletRequest request) throws IOException {
securityValidationService.validatePassword(request);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package ua.everybuy.service.advertisement.user.impl;

import jakarta.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import ua.everybuy.database.entity.City;
import ua.everybuy.database.repository.advertisement.AdvertisementRepository;
import ua.everybuy.service.advertisement.user.UserLastLocationService;
import ua.everybuy.service.location.CityService;

@Service
@RequiredArgsConstructor
public class AdvertisementUserLastLocationService implements UserLastLocationService {
private final AdvertisementRepository advertisementRepository;
private final CityService cityService;

@Override
public City getLastLocationForUser(Long userId) {
return cityService.findById(getLastLocationId(userId));
}

private Long getLastLocationId(Long userId) {
return advertisementRepository.findFirstCityIdByUserId(userId)
.orElseThrow(() -> new EntityNotFoundException(
"No advertisements found for user ID: "
+ userId + ", cannot determine last location"));
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ua.everybuy.service.advertisement.user;
package ua.everybuy.service.advertisement.user.impl;

import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
Expand All @@ -7,6 +7,7 @@
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import ua.everybuy.service.advertisement.user.UserProfileService;
import ua.everybuy.service.category.CategoryService;
import ua.everybuy.service.advertisement.filter.sort.SortStrategyFactory;
import ua.everybuy.database.entity.Advertisement;
Expand All @@ -21,14 +22,15 @@

@Service
@RequiredArgsConstructor
public class AdvertisementUserProfileService {
public class AdvertisementUserProfileService implements UserProfileService {
private final AdvertisementUserDtoBuilder userDtoBuilder;
private final AdvertisementUserFilterSpecificationFactory advertisementUserSpecificationFactory;
private final AdvertisementUserSpecificationFactory advertisementSpecificationFactory;
private final AdvertisementRepository advertisementRepository;
private final CategoryService categoryService;
private final SortStrategyFactory sortStrategyFactory;

@Override
public UserAdvertisementDto getUserActiveFilteredAdvertisements(Long userId, Long categoryId,
Advertisement.AdSection section,
int page, int size) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ua.everybuy.service.advertisement.user;
package ua.everybuy.service.advertisement.user.impl;

import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
Expand All @@ -10,15 +10,18 @@
import ua.everybuy.database.entity.Advertisement;
import ua.everybuy.routing.mapper.AdvertisementResponseMapper;
import ua.everybuy.routing.dto.response.AdvertisementWithStatisticResponse;
import ua.everybuy.service.advertisement.user.UserStatisticService;

import java.util.List;

@Service
@RequiredArgsConstructor
public class AdvertisementUserStatisticService {
public class AdvertisementUserStatisticService implements UserStatisticService {
private final AdvertisementStorageService advertisementStorageService;
private final AdvertisementResponseMapper advertisementResponseMapper;
private final SortStrategyFactory sortStrategyFactory;

@Override
public List<AdvertisementWithStatisticResponse> getUserAdvertisementsWithStatistic(Long userId, boolean isEnabled,
Advertisement.AdSection section,
int page, int size) {
Expand Down
Loading