Zadania 1, 2, 9

This commit is contained in:
2026-08-18 15:33:53 +02:00
parent 571ec05d76
commit c52bbabec0
8 changed files with 441 additions and 27 deletions
@@ -78,10 +78,17 @@ public class SchemaFixer {
"ALTER TABLE IF EXISTS user_reports ADD COLUMN IF NOT EXISTS reported_user_deleted BOOLEAN NOT NULL DEFAULT FALSE"
);
// Blokowanie konta ze zgloszenia zostalo usuniete - encja nie mapuje juz tej kolumny,
// wiec stare wiersze zostaja, a nowe wpisy wypelnia wartosc domyslna.
// Blokowanie konta ze zgloszenia zostalo usuniete - encja nie mapuje juz tej kolumny.
// W starych bazach kolumna zostaje, wiec nadajemy jej wartosc domyslna; w nowych jej nie ma,
// a ALTER COLUMN nie przyjmuje IF EXISTS - stad sprawdzenie w bloku DO.
jdbcTemplate.execute(
"ALTER TABLE IF EXISTS user_reports ALTER COLUMN reported_user_blocked SET DEFAULT FALSE"
"DO $$ "
+ "BEGIN "
+ " IF EXISTS (SELECT 1 FROM information_schema.columns "
+ " WHERE table_name = 'user_reports' AND column_name = 'reported_user_blocked') THEN "
+ " ALTER TABLE user_reports ALTER COLUMN reported_user_blocked SET DEFAULT FALSE; "
+ " END IF; "
+ "END $$"
);
// Widocznosc profilu ma juz tylko dwie wartosci - wariant "tylko kontakty" zostal usuniety.
@@ -43,6 +43,8 @@ public record ListingCreateRequest(
List<String> media,
List<String> amenities,
List<String> photos,
String virtualTourUrl
String virtualTourUrl,
// true = publikujemy tylko przyblizona lokalizacje (mapa rysuje obszar zamiast punktu)
Boolean approximateLocation
) {
}
@@ -54,9 +54,30 @@ public record ListingDetailResponse(
ListingStatus status,
Instant createdAt,
Long viewsCount,
Instant promotedUntil
Instant promotedUntil,
// true = wlasciciel nie publikuje dokladnego adresu; mapa rysuje obszar zamiast punktu
boolean approximateLocation
) {
// Promien obszaru, w ktorym mieszcza sie przesuniete wspolrzedne - mapa rysuje kolo tej wielkosci.
private static final double APPROXIMATE_RADIUS_M = 300d;
private static final double METERS_PER_DEGREE = 111_320d;
public static ListingDetailResponse from(PropertyListing listing, Long ownerId, String ownerAvatar) {
return from(listing, ownerId, ownerAvatar, false);
}
/**
* Gdy {@code masked} jest true (ogloszenie z ukryta lokalizacja ogladane przez kogos innego niz
* wlasciciel i administracja), z odpowiedzi znikaja ulica oraz numer budynku, a wspolrzedne sa
* przesuwane w obrebie obszaru. Przesuniecie liczymy z identyfikatora ogloszenia, wiec dla danej
* oferty jest zawsze takie samo - inaczej kolo skakaloby po mapie przy kazdym odswiezeniu.
*/
public static ListingDetailResponse from(PropertyListing listing, Long ownerId, String ownerAvatar, boolean masked) {
String street = masked ? null : listing.getStreet();
String building = masked ? null : listing.getBuilding();
String address = masked ? approximateAddress(listing) : listing.getAddress();
Double lat = masked ? blurLat(listing) : listing.getLat();
Double lng = masked ? blurLng(listing) : listing.getLng();
return new ListingDetailResponse(
listing.getId(),
listing.getTitle(),
@@ -65,9 +86,9 @@ public record ListingDetailResponse(
listing.getPropertyType(),
listing.getCity(),
listing.getDistrict(),
listing.getStreet(),
listing.getBuilding(),
listing.getAddress(),
street,
building,
address,
listing.getPrice(),
listing.getArea(),
listing.getRooms(),
@@ -88,8 +109,8 @@ public record ListingDetailResponse(
listing.getWindows(),
listing.getExposure(),
listing.getRoomHeight(),
listing.getLat(),
listing.getLng(),
lat,
lng,
List.copyOf(listing.getMedia()),
List.copyOf(listing.getAmenities()),
List.copyOf(listing.getPhotos()),
@@ -100,7 +121,42 @@ public record ListingDetailResponse(
listing.getStatus(),
listing.getCreatedAt(),
listing.getViewsCount(),
listing.getPromotedUntil()
listing.getPromotedUntil(),
listing.isApproximateLocation()
);
}
// Bez ulicy i numeru zostaje sama okolica - dzielnica i miasto.
private static String approximateAddress(PropertyListing listing) {
String district = listing.getDistrict();
return district == null || district.isBlank() ? listing.getCity() : district + ", " + listing.getCity();
}
private static Double blurLat(PropertyListing listing) {
if (listing.getLat() == null) {
return null;
}
return listing.getLat() + (offsetMeters(listing) * Math.cos(offsetAngle(listing))) / METERS_PER_DEGREE;
}
private static Double blurLng(PropertyListing listing) {
if (listing.getLng() == null || listing.getLat() == null) {
return null;
}
double parallel = METERS_PER_DEGREE * Math.cos(Math.toRadians(listing.getLat()));
if (parallel == 0) {
return listing.getLng();
}
return listing.getLng() + (offsetMeters(listing) * Math.sin(offsetAngle(listing))) / parallel;
}
// Kierunek i dlugosc przesuniecia sa stale dla ogloszenia, a punkt zostaje w polowie promienia,
// wiec rzeczywisty adres zawsze miesci sie wewnatrz rysowanego kola.
private static double offsetAngle(PropertyListing listing) {
return Math.toRadians((listing.getId() * 137L) % 360);
}
private static double offsetMeters(PropertyListing listing) {
return (listing.getId() * 53L) % (long) (APPROXIMATE_RADIUS_M / 2);
}
}
@@ -31,6 +31,18 @@ public record ListingResponse(
Instant createdAt
) {
public static ListingResponse from(PropertyListing listing) {
return from(listing, false);
}
/**
* @param masked pokaz sama okolice zamiast adresu - dla ogloszen z ukryta dokladna lokalizacja
* ogladanych spoza panelu wlasciciela i administracji.
*/
public static ListingResponse from(PropertyListing listing, boolean masked) {
String district = listing.getDistrict();
String address = masked
? (district == null || district.isBlank() ? listing.getCity() : district + ", " + listing.getCity())
: listing.getAddress();
return new ListingResponse(
listing.getId(),
listing.getTitle(),
@@ -38,8 +50,8 @@ public record ListingResponse(
listing.getOfferType(),
listing.getPropertyType(),
listing.getCity(),
listing.getDistrict(),
listing.getAddress(),
district,
address,
listing.getPrice(),
listing.getArea(),
listing.getRooms(),
@@ -51,11 +51,19 @@ public class ListingService {
* Ogloszenia demonstracyjne nie maja konta w serwisie - wtedy oba pola sa puste.
*/
private ListingDetailResponse detailOf(PropertyListing listing) {
return detailOf(listing, false);
}
/**
* @param masked ukryj dokladny adres (ulica, numer, wspolrzedne) - dla ogloszen, ktorych
* wlasciciel wybral publikowanie samej przyblizonej lokalizacji.
*/
private ListingDetailResponse detailOf(PropertyListing listing, boolean masked) {
AppUser owner = ownerOf(listing);
if (owner == null) {
return ListingDetailResponse.from(listing, null, null);
return ListingDetailResponse.from(listing, null, null, masked);
}
return ListingDetailResponse.from(listing, owner.getId(), userSettingsService.avatarFor(owner.getId()));
return ListingDetailResponse.from(listing, owner.getId(), userSettingsService.avatarFor(owner.getId()), masked);
}
/**
@@ -85,7 +93,7 @@ public class ListingService {
.filter(listing -> offerType == null || listing.getOfferType() == offerType)
.filter(listing -> propertyType == null || listing.getPropertyType() == propertyType)
.sorted(promotedFirst())
.map(ListingResponse::from)
.map(listing -> ListingResponse.from(listing, listing.isApproximateLocation()))
.toList();
}
@@ -110,7 +118,8 @@ public class ListingService {
listing = listingRepository.save(listing);
}
return detailOf(listing);
// Wlasciciel i administracja widza pelny adres - reszta tylko przyblizona lokalizacje.
return detailOf(listing, listing.isApproximateLocation() && !isOwner && !viewerIsAdmin);
}
public List<ListingResponse> findMine(String ownerEmail) {
@@ -222,6 +231,7 @@ public class ListingService {
listing.setRoomHeight(trimToNull(request.roomHeight()));
listing.setLat(request.lat());
listing.setLng(request.lng());
listing.setApproximateLocation(Boolean.TRUE.equals(request.approximateLocation()));
listing.setMedia(cleanList(request.media()));
listing.setAmenities(cleanList(request.amenities()));
@@ -17,6 +17,7 @@ import java.math.BigDecimal;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.annotations.ColumnDefault;
@Entity
@Table(name = "property_listings")
@@ -121,6 +122,12 @@ public class PropertyListing {
private Double lng;
// Wlasciciel nie chce publikowac dokladnego adresu - mapa pokazuje wtedy przyblizony obszar,
// a odpowiedzi publiczne nie zawieraja ulicy ani numeru budynku.
@Column(nullable = false)
@ColumnDefault("false")
private boolean approximateLocation = false;
@ElementCollection
@CollectionTable(name = "listing_media", joinColumns = @JoinColumn(name = "listing_id"))
@Column(name = "value", length = 60)
@@ -420,6 +427,14 @@ public class PropertyListing {
this.lng = lng;
}
public boolean isApproximateLocation() {
return approximateLocation;
}
public void setApproximateLocation(boolean approximateLocation) {
this.approximateLocation = approximateLocation;
}
public List<String> getMedia() {
return media;
}