Add: full basic Huertos functionality

This commit is contained in:
Jose
2026-01-21 11:03:15 +01:00
parent 21281b10cc
commit e95d5a0793
69 changed files with 1683 additions and 257 deletions

View File

@@ -33,6 +33,12 @@
<artifactId>mariadb-java-client</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-http-client</artifactId>
<version>4.0.1</version>
<scope>compile</scope>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>

View File

@@ -3,7 +3,10 @@ package net.miarma.backend.huertos;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@SpringBootApplication(scanBasePackages = {
"net.miarma.backend.huertos",
"net.miarma.backlib"
})
public class HuertosApplication {
public static void main(String[] args) {
SpringApplication.run(HuertosApplication.class, args);

View File

@@ -0,0 +1,53 @@
package net.miarma.backend.huertos.client;
import net.miarma.backlib.dto.LoginRequest;
import net.miarma.backlib.dto.LoginResponse;
import net.miarma.backlib.dto.UserWithCredentialDto;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
@Component
public class HuertosWebClient {
private final RestTemplate restTemplate;
private final String coreUrl;
public HuertosWebClient(RestTemplate restTemplate,
@Value("${core.url}") String coreUrl) {
this.restTemplate = restTemplate;
this.coreUrl = coreUrl;
}
public String login(String username, String password) {
LoginRequest req = new LoginRequest(username, password, (byte) 1);
LoginResponse resp = restTemplate.postForObject(
coreUrl + "/auth/login",
req,
LoginResponse.class
);
return resp.token();
}
public UserWithCredentialDto getUserWithCredential(UUID userId, Byte serviceId) {
return restTemplate.getForObject(
coreUrl + "/users/{user_id}/service/{service_id}",
UserWithCredentialDto.class,
userId, serviceId
);
}
public List<UserWithCredentialDto> getAllUsersWithCredentials(Byte serviceId) {
UserWithCredentialDto[] arr = restTemplate.getForObject(
coreUrl + "/users/service/{service_id}",
UserWithCredentialDto[].class,
serviceId
);
return arr == null ? List.of() : Arrays.asList(arr);
}
}

View File

@@ -0,0 +1,37 @@
package net.miarma.backend.huertos.config;
import net.miarma.backend.huertos.client.HuertosWebClient;
import net.miarma.backlib.security.CoreAuthTokenHolder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class HuertosStartup implements ApplicationRunner {
private final HuertosWebClient client;
private final CoreAuthTokenHolder tokenHolder;
private final String user;
private final String pass;
public HuertosStartup(
HuertosWebClient client,
CoreAuthTokenHolder tokenHolder,
@Value("${huertos.user}") String user,
@Value("${huertos.password}") String pass
) {
this.client = client;
this.tokenHolder = tokenHolder;
this.user = user;
this.pass = pass;
}
@Override
public void run(ApplicationArguments args) {
String token = client.login(user, pass);
tokenHolder.setToken(token);
System.out.println("TOKEN recibido: " + token);
System.out.println("HUERTOS CLIENT has been authenticated in CORE");
}
}

View File

@@ -0,0 +1,35 @@
package net.miarma.backend.huertos.config;
import net.miarma.backlib.security.CoreAuthTokenHolder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(CoreAuthTokenHolder tokenHolder) {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(authInterceptor(tokenHolder));
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
private ClientHttpRequestInterceptor authInterceptor(CoreAuthTokenHolder tokenHolder) {
return (request, body, execution) -> {
String token = tokenHolder.getToken();
if (token != null) {
request.getHeaders().add("Authorization", "Bearer " + token);
}
return execution.execute(request, body);
};
}
}

View File

@@ -27,13 +27,14 @@ public class SecurityConfig {
.csrf(csrf -> csrf.disable())
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
// PUBLICAS
.requestMatchers("/login").permitAll()
.requestMatchers("/announces/**").permitAll()
.requestMatchers("/huertos/members/waitlist").permitAll()
.requestMatchers("/huertos/members/latest-number").permitAll()
// PRIVADAS
.requestMatchers("/**").authenticated()
// PUBLICAS
.requestMatchers("/login").permitAll()
.requestMatchers("/announces/**").permitAll()
.requestMatchers("/huertos/members/waitlist").permitAll()
.requestMatchers("/huertos/members/waitlist/limited").permitAll()
.requestMatchers("/huertos/members/latest-number").permitAll()
// PRIVADAS
.requestMatchers("/**").authenticated()
);
http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();

View File

@@ -0,0 +1,60 @@
package net.miarma.backend.huertos.controller;
import net.miarma.backend.huertos.dto.AnnouncementDto;
import net.miarma.backend.huertos.mapper.AnnouncementMapper;
import net.miarma.backend.huertos.model.Announcement;
import net.miarma.backend.huertos.service.AnnouncementService;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@RestController
@RequestMapping("/announcements")
public class AnnouncementController {
private final AnnouncementService announcementService;
public AnnouncementController(AnnouncementService announcementService) {
this.announcementService = announcementService;
}
@GetMapping
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<List<AnnouncementDto.Response>> getAll() {
return ResponseEntity.ok(
announcementService.getAll()
.stream()
.map(AnnouncementMapper::toResponse)
.toList()
);
}
@GetMapping("/{announce_id}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<AnnouncementDto.Response> getById(@PathVariable("announce_id") UUID announcementId) {
Announcement announcement = announcementService.getById(announcementId);
return ResponseEntity.ok(AnnouncementMapper.toResponse(announcement));
}
@PutMapping("/{announce_id}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<AnnouncementDto.Response> update(
@PathVariable("announce_id") UUID announcementId,
@RequestBody AnnouncementDto.Request dto
) {
return ResponseEntity.ok(
AnnouncementMapper.toResponse(announcementService.update(announcementId, dto))
);
}
@DeleteMapping("/{announce_id}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<Map<String, String>> delete(@PathVariable("announce_id") UUID announcementId) {
announcementService.delete(announcementId);
return ResponseEntity.ok(Map.of("message", "Deleted announcement: " + announcementId));
}
}

View File

@@ -0,0 +1,28 @@
package net.miarma.backend.huertos.controller;
import net.miarma.backend.huertos.dto.BalanceDto;
import net.miarma.backend.huertos.mapper.BalanceMapper;
import net.miarma.backend.huertos.model.Balance;
import net.miarma.backend.huertos.service.BalanceService;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/balance")
public class BalanceController {
private final BalanceService balanceService;
public BalanceController(BalanceService balanceService) {
this.balanceService = balanceService;
}
@GetMapping
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<BalanceDto> getBalance() {
Balance balance = balanceService.get();
return ResponseEntity.ok(BalanceMapper.toDto(balance));
}
}

View File

@@ -0,0 +1,54 @@
package net.miarma.backend.huertos.controller;
import net.miarma.backend.huertos.dto.ExpenseDto;
import net.miarma.backend.huertos.mapper.ExpenseMapper;
import net.miarma.backend.huertos.model.Expense;
import net.miarma.backend.huertos.service.ExpenseService;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@RestController
@RequestMapping("/expenses")
public class ExpenseController {
private ExpenseService expenseService;
public ExpenseController(ExpenseService expenseService) {
this.expenseService = expenseService;
}
@GetMapping
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<List<ExpenseDto.Response>> getAll() {
return ResponseEntity.ok(
expenseService.getAll()
.stream()
.map(ExpenseMapper::toResponse)
.toList()
);
}
@GetMapping("/{expense_id}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<ExpenseDto.Response> getById(@PathVariable("expense_id") UUID expenseId) {
Expense expense = expenseService.getById(expenseId);
return ResponseEntity.ok(ExpenseMapper.toResponse(expense));
}
@PutMapping("/{expense_id}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<ExpenseDto.Response> update(@PathVariable("expense_id") UUID expenseId, @RequestBody ExpenseDto.Request dto) {
return ResponseEntity.ok(ExpenseMapper.toResponse(expenseService.update(expenseId, dto)));
}
@DeleteMapping("/{expense_id}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<Map<String,String>> delete(@PathVariable("expense_id") UUID expenseId) {
expenseService.delete(expenseId);
return ResponseEntity.ok(Map.of("message", "Deleted expense: " + expenseId));
}
}

View File

@@ -0,0 +1,97 @@
package net.miarma.backend.huertos.controller;
import net.miarma.backend.huertos.dto.IncomeDto;
import net.miarma.backend.huertos.dto.view.VIncomesWithFullNamesDto;
import net.miarma.backend.huertos.mapper.IncomeMapper;
import net.miarma.backend.huertos.mapper.view.VIncomesWithFullNamesMapper;
import net.miarma.backend.huertos.model.Income;
import net.miarma.backend.huertos.service.IncomeService;
import net.miarma.backend.huertos.service.view.VIncomesWithFullNamesService;
import net.miarma.backlib.security.JwtService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@RestController
@RequestMapping("/incomes")
public class IncomeController {
private IncomeService incomeService;
private VIncomesWithFullNamesService vIncomesWithFullNamesService;
private JwtService jwtService;
public IncomeController(IncomeService incomeService, VIncomesWithFullNamesService vIncomesWithFullNamesService, JwtService jwtService) {
this.incomeService = incomeService;
this.vIncomesWithFullNamesService = vIncomesWithFullNamesService;
this.jwtService = jwtService;
}
@GetMapping
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<List<IncomeDto.Response>> getAll() {
return ResponseEntity.ok(
incomeService.getAll()
.stream()
.map(IncomeMapper::toResponse)
.toList()
);
}
@GetMapping("/with-names")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<List<VIncomesWithFullNamesDto>> getAllWithNames() {
return ResponseEntity.ok(
vIncomesWithFullNamesService.getAll()
.stream()
.map(VIncomesWithFullNamesMapper::toResponse)
.toList()
);
}
@GetMapping("/mine")
public ResponseEntity<List<IncomeDto.Response>> getMine(@RequestHeader("Authorization") String authHeader) {
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
String token = authHeader.substring(7);
UUID userId;
try {
userId = jwtService.getUserId(token);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
return ResponseEntity.ok(
incomeService.getByUserId(userId)
.stream()
.map(IncomeMapper::toResponse)
.toList()
);
}
@GetMapping("/{income_id}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<IncomeDto.Response> getById(@PathVariable("income_id") UUID incomeId) {
Income income = incomeService.getById(incomeId);
return ResponseEntity.ok(IncomeMapper.toResponse(income));
}
@PutMapping("/{income_id}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<IncomeDto.Response> update(@PathVariable("income_id") UUID incomeId, @RequestBody IncomeDto.Request dto) {
return ResponseEntity.ok(IncomeMapper.toResponse(incomeService.update(incomeId, dto)));
}
@DeleteMapping("/{income_id}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<Map<String,String>> delete(@PathVariable("income_id") UUID incomeId) {
incomeService.delete(incomeId);
return ResponseEntity.ok(Map.of("message", "Deleted income: " + incomeId));
}
}

View File

@@ -0,0 +1,122 @@
package net.miarma.backend.huertos.controller;
import net.miarma.backend.huertos.dto.MemberDto;
import net.miarma.backend.huertos.dto.WaitlistCensoredDto;
import net.miarma.backend.huertos.security.HuertosPrincipal;
import net.miarma.backend.huertos.service.MemberService;
import net.miarma.backlib.security.JwtService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.UUID;
@RestController
@RequestMapping("/users")
public class MemberController {
private final MemberService memberService;
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
private Byte getServiceIdFromToken() {
var auth = SecurityContextHolder.getContext().getAuthentication();
if (auth == null || !(auth.getPrincipal() instanceof HuertosPrincipal principal)) {
return null;
}
return principal.getServiceId();
}
@GetMapping
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public List<MemberDto> getAll() {
return memberService.getAll(getServiceIdFromToken());
}
@GetMapping("/{user_id}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public MemberDto getById(@PathVariable("user_id") UUID userId) {
return memberService.getById(userId, getServiceIdFromToken());
}
@GetMapping("/me")
public MemberDto getMe() {
HuertosPrincipal principal =
(HuertosPrincipal) SecurityContextHolder
.getContext()
.getAuthentication()
.getPrincipal();
return memberService.getById(principal.getUserId(), getServiceIdFromToken());
}
@GetMapping("/latest-number")
public Integer getLatestNumber() {
return memberService.getLatestMemberNumber();
}
@GetMapping("/waitlist")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public List<MemberDto> getWaitlist() {
return memberService.getWaitlist();
}
@GetMapping("/waitlist/limited")
public List<WaitlistCensoredDto> getWaitlistLimited() {
return memberService.getWaitlistLimited();
}
@GetMapping("/number/{member_number}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public MemberDto getByMemberNumber(@PathVariable("member_number") Integer memberNumber) {
return memberService.getByMemberNumber(memberNumber);
}
@GetMapping("/number/{member_number}/incomes")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public Boolean getMemberIncomes(@PathVariable("member_number") Integer memberNumber) {
return memberService.hasIncomes(memberNumber);
}
@GetMapping("/number/{member_number}/has-paid")
public Boolean getMemberHasPaid(@PathVariable("member_number") Integer memberNumber) {
return memberService.hasPaid(memberNumber);
}
@GetMapping("/number/{member_number}/has-collaborator")
public Boolean getMemberHasCollaborator(@PathVariable("member_number") Integer memberNumber) {
return memberService.hasCollaborator(memberNumber);
}
@GetMapping("/number/{member_number}/has-greenhouse")
public Boolean getMemberHasGreenhouse(@PathVariable("member_number") Integer memberNumber) {
return memberService.hasGreenhouse(memberNumber);
}
@GetMapping("/number/{member_number}/has-collaborator-request")
public Boolean getMemberHasCollaboratorRequest(@PathVariable("member_number") Integer memberNumber) {
return memberService.hasCollaboratorRequest(memberNumber);
}
@GetMapping("/number/{member_number}/has-greenhouse-request")
public Boolean getMemberHasGreenhouseRequest(@PathVariable("member_number") Integer memberNumber) {
return memberService.hasGreenhouseRequest(memberNumber);
}
@GetMapping("/plot/{plot_number}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public MemberDto getByPlotNumber(@PathVariable("plot_number") Integer plotNumber) {
return memberService.getByPlotNumber(plotNumber);
}
@GetMapping("/dni/{dni}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public MemberDto getByDni(@PathVariable("dni") String dni) {
return memberService.getByDni(dni);
}
}

View File

@@ -0,0 +1,60 @@
package net.miarma.backend.huertos.controller;
import net.miarma.backend.huertos.dto.PreUserDto;
import net.miarma.backend.huertos.mapper.PreUserMapper;
import net.miarma.backend.huertos.model.PreUser;
import net.miarma.backend.huertos.service.PreUserService;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@RestController
@RequestMapping("/pre_users")
public class PreUserController {
private final PreUserService preUserService;
public PreUserController(PreUserService preUserService) {
this.preUserService = preUserService;
}
@GetMapping
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<List<PreUserDto.Response>> getAll() {
return ResponseEntity.ok(
preUserService.getAll()
.stream()
.map(PreUserMapper::toResponse)
.toList()
);
}
@GetMapping("/{pre_user_id}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<PreUserDto.Response> getById(@PathVariable("pre_user_id") UUID preUserId) {
PreUser preUser = preUserService.getById(preUserId);
return ResponseEntity.ok(PreUserMapper.toResponse(preUser));
}
@PutMapping("/{pre_user_id}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<PreUserDto.Response> update(
@PathVariable("pre_user_id") UUID preUserId,
@RequestBody PreUserDto.Request dto
) {
return ResponseEntity.ok(
PreUserMapper.toResponse(preUserService.update(preUserId, dto))
);
}
@DeleteMapping("/{pre_user_id}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<Map<String, String>> delete(@PathVariable("pre_user_id") UUID preUserId) {
preUserService.delete(preUserId);
return ResponseEntity.ok(Map.of("message", "Deleted pre_user: " + preUserId));
}
}

View File

@@ -0,0 +1,142 @@
package net.miarma.backend.huertos.controller;
import net.miarma.backend.huertos.dto.RequestCountDto;
import net.miarma.backend.huertos.dto.RequestDto;
import net.miarma.backend.huertos.dto.view.VRequestsWithPreUsersDto;
import net.miarma.backend.huertos.mapper.RequestMapper;
import net.miarma.backend.huertos.mapper.view.VIncomesWithFullNamesMapper;
import net.miarma.backend.huertos.mapper.view.VRequestsWithPreUsersMapper;
import net.miarma.backend.huertos.model.Request;
import net.miarma.backend.huertos.model.view.VRequestsWithPreUsers;
import net.miarma.backend.huertos.service.RequestService;
import net.miarma.backend.huertos.service.view.VRequestsWithPreUsersService;
import net.miarma.backlib.security.JwtService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/requests")
public class RequestController {
private final RequestService requestService;
private final VRequestsWithPreUsersService vRequestsWithPreUsersService;
private final JwtService jwtService;
public RequestController(RequestService requestService, VRequestsWithPreUsersService vRequestsWithPreUsersService, JwtService jwtService) {
this.requestService = requestService;
this.vRequestsWithPreUsersService = vRequestsWithPreUsersService;
this.jwtService = jwtService;
}
@GetMapping
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<List<RequestDto.Response>> getAll() {
return ResponseEntity.ok(
requestService.getAll()
.stream()
.map(RequestMapper::toResponse)
.toList()
);
}
@GetMapping("/count")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<RequestCountDto> getRequestCount() {
return ResponseEntity.ok(
requestService.getAll()
.stream()
.map(RequestMapper::toResponse)
.collect(Collectors.collectingAndThen(
Collectors.counting(),
RequestCountDto::new
))
);
}
@GetMapping("/mine")
public ResponseEntity<List<RequestDto.Response>> getMine(@RequestHeader("Authorization") String authHeader) {
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
String token = authHeader.substring(7);
UUID userId;
try {
userId = jwtService.getUserId(token);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
return ResponseEntity.ok(
requestService.getByUserId(userId)
.stream()
.map(RequestMapper::toResponse)
.toList()
);
}
@GetMapping("/full")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<List<VRequestsWithPreUsersDto>> getAllWithPreUsers() {
return ResponseEntity.ok(
vRequestsWithPreUsersService.getAll()
.stream()
.map(VRequestsWithPreUsersMapper::toResponse)
.toList()
);
}
@GetMapping("/full/{request_id}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<VRequestsWithPreUsersDto> getByIdWithPreUsers(@PathVariable("request_id") UUID requestId) {
VRequestsWithPreUsers request = vRequestsWithPreUsersService.getById(requestId);
return ResponseEntity.ok(VRequestsWithPreUsersMapper.toResponse(request));
}
@GetMapping("/{request_id}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<RequestDto.Response> getById(@PathVariable("request_id") UUID requestId) {
Request request = requestService.getById(requestId);
return ResponseEntity.ok(RequestMapper.toResponse(request));
}
@PutMapping("/{request_id}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<RequestDto.Response> update(
@PathVariable("request_id") UUID requestId,
@RequestBody RequestDto.Request dto
) {
return ResponseEntity.ok(
RequestMapper.toResponse(requestService.update(requestId, dto))
);
}
@PutMapping("/{request_id}/accept")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<Map<String, String>> acceptRequest(@PathVariable("request_id") UUID requestId) {
requestService.acceptRequest(requestId);
return ResponseEntity.ok(Map.of("message", "Accepted request: " + requestId));
}
@PutMapping("/{request_id}/reject")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<Map<String, String>> rejectRequest(@PathVariable("request_id") UUID requestId) {
requestService.rejectRequest(requestId);
return ResponseEntity.ok(Map.of("message", "Denied request: " + requestId));
}
@DeleteMapping("/{request_id}")
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
public ResponseEntity<Map<String, String>> delete(@PathVariable("request_id") UUID requestId) {
requestService.delete(requestId);
return ResponseEntity.ok(Map.of("message", "Deleted request: " + requestId));
}
}

View File

@@ -0,0 +1,8 @@
package net.miarma.backend.huertos.dto;
import net.miarma.backlib.dto.CredentialDto;
import net.miarma.backlib.dto.UserDto;
public record MemberDto(UserDto user,
CredentialDto account,
HuertosUserMetadataDto metadata) {}

View File

@@ -0,0 +1,5 @@
package net.miarma.backend.huertos.dto;
public record RequestCountDto(Long count) {
}

View File

@@ -0,0 +1,13 @@
package net.miarma.backend.huertos.dto;
public class WaitlistCensoredDto {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@@ -1,70 +1,71 @@
package net.miarma.backend.huertos.dto.view;
import java.math.BigDecimal;
import java.time.Instant;
public class VBalanceWithTotalsDto {
private Long id;
private Double initialBank;
private Double initialCash;
private Double totalBankExpenses;
private Double totalCashExpenses;
private Double totalBankIncomes;
private Double totalCashIncomes;
private Byte id;
private BigDecimal initialBank;
private BigDecimal initialCash;
private BigDecimal totalBankExpenses;
private BigDecimal totalCashExpenses;
private BigDecimal totalBankIncomes;
private BigDecimal totalCashIncomes;
private Instant createdAt;
public Long getId() {
public Byte getId() {
return id;
}
public void setId(Long id) {
public void setId(Byte id) {
this.id = id;
}
public Double getInitialBank() {
public BigDecimal getInitialBank() {
return initialBank;
}
public void setInitialBank(Double initialBank) {
public void setInitialBank(BigDecimal initialBank) {
this.initialBank = initialBank;
}
public Double getInitialCash() {
public BigDecimal getInitialCash() {
return initialCash;
}
public void setInitialCash(Double initialCash) {
public void setInitialCash(BigDecimal initialCash) {
this.initialCash = initialCash;
}
public Double getTotalBankExpenses() {
public BigDecimal getTotalBankExpenses() {
return totalBankExpenses;
}
public void setTotalBankExpenses(Double totalBankExpenses) {
public void setTotalBankExpenses(BigDecimal totalBankExpenses) {
this.totalBankExpenses = totalBankExpenses;
}
public Double getTotalCashExpenses() {
public BigDecimal getTotalCashExpenses() {
return totalCashExpenses;
}
public void setTotalCashExpenses(Double totalCashExpenses) {
public void setTotalCashExpenses(BigDecimal totalCashExpenses) {
this.totalCashExpenses = totalCashExpenses;
}
public Double getTotalBankIncomes() {
public BigDecimal getTotalBankIncomes() {
return totalBankIncomes;
}
public void setTotalBankIncomes(Double totalBankIncomes) {
public void setTotalBankIncomes(BigDecimal totalBankIncomes) {
this.totalBankIncomes = totalBankIncomes;
}
public Double getTotalCashIncomes() {
public BigDecimal getTotalCashIncomes() {
return totalCashIncomes;
}
public void setTotalCashIncomes(Double totalCashIncomes) {
public void setTotalCashIncomes(BigDecimal totalCashIncomes) {
this.totalCashIncomes = totalCashIncomes;
}

View File

@@ -3,7 +3,7 @@ package net.miarma.backend.huertos.dto.view;
import java.time.Instant;
import java.util.UUID;
public class VHuertosMemberDto {
public class VHuertosMembersDto {
private UUID userId;
private String displayName;
private String avatar;

View File

@@ -1,5 +1,6 @@
package net.miarma.backend.huertos.dto.view;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.UUID;
@@ -8,7 +9,7 @@ public class VIncomesWithFullNamesDto {
private UUID userId;
private String displayName;
private String concept;
private Double amount;
private BigDecimal amount;
private Byte type;
private Byte frequency;
private Instant createdAt;
@@ -45,11 +46,11 @@ public class VIncomesWithFullNamesDto {
this.concept = concept;
}
public Double getAmount() {
public BigDecimal getAmount() {
return amount;
}
public void setAmount(Double amount) {
public void setAmount(BigDecimal amount) {
this.amount = amount;
}

View File

@@ -8,7 +8,7 @@ import java.util.UUID;
public class AnnouncementMapper {
public static AnnouncementDto.Response toDto(Announcement entity) {
public static AnnouncementDto.Response toResponse(Announcement entity) {
AnnouncementDto.Response dto = new AnnouncementDto.Response();
dto.setAnnounceId(entity.getAnnounceId());
dto.setBody(entity.getBody());

View File

@@ -1,12 +1,12 @@
package net.miarma.backend.huertos.mapper.view;
import net.miarma.backend.huertos.dto.view.VHuertosMemberDto;
import net.miarma.backend.huertos.model.view.VHuertosMember;
import net.miarma.backend.huertos.dto.view.VHuertosMembersDto;
import net.miarma.backend.huertos.model.view.VHuertosMembers;
public class VHuertosMemberMapper {
public class VHuertosMembersMapper {
public static VHuertosMemberDto toDto(VHuertosMember entity) {
VHuertosMemberDto dto = new VHuertosMemberDto();
public static VHuertosMembersDto toResponse(VHuertosMembers entity) {
VHuertosMembersDto dto = new VHuertosMembersDto();
dto.setUserId(entity.getUserId());
dto.setDisplayName(entity.getDisplayName());
dto.setAvatar(entity.getAvatar());

View File

@@ -5,7 +5,7 @@ import net.miarma.backend.huertos.model.view.VIncomesWithFullNames;
public class VIncomesWithFullNamesMapper {
public static VIncomesWithFullNamesDto toDto(VIncomesWithFullNames entity) {
public static VIncomesWithFullNamesDto toResponse(VIncomesWithFullNames entity) {
VIncomesWithFullNamesDto dto = new VIncomesWithFullNamesDto();
dto.setIncomeId(entity.getIncomeId());
dto.setUserId(entity.getUserId());

View File

@@ -5,7 +5,7 @@ import net.miarma.backend.huertos.model.view.VRequestsWithPreUsers;
public class VRequestsWithPreUsersMapper {
public static VRequestsWithPreUsersDto toDto(VRequestsWithPreUsers entity) {
public static VRequestsWithPreUsersDto toResponse(VRequestsWithPreUsers entity) {
VRequestsWithPreUsersDto dto = new VRequestsWithPreUsersDto();
dto.setRequestId(entity.getRequestId());
dto.setRequestType(entity.getRequestType());

View File

@@ -1,7 +1,9 @@
package net.miarma.backend.huertos.model;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.UUID;
import jakarta.persistence.*;
@@ -62,6 +64,17 @@ public class Income {
}
}
public boolean isPaid() {
Instant now = Instant.now();
if (frequency == 0) { // BIYEARLY
return Duration.between(createdAt, now).toDays() <= 6L * 30;
} else if (frequency == 1) { // YEARLY
return Duration.between(createdAt, now).toDays() <= 12L * 30;
} else {
return false;
}
}
public UUID getIncomeId() {
return incomeId;
}

View File

@@ -6,6 +6,7 @@ import jakarta.persistence.Id;
import jakarta.persistence.Table;
import org.hibernate.annotations.Immutable;
import java.math.BigDecimal;
import java.time.Instant;
@Entity
@@ -14,54 +15,54 @@ import java.time.Instant;
public class VBalanceWithTotals {
@Id
private Long id;
private Byte id;
@Column(name = "initial_bank")
private Double initialBank;
private BigDecimal initialBank;
@Column(name = "initial_cash")
private Double initialCash;
private BigDecimal initialCash;
@Column(name = "total_bank_expenses")
private Double totalBankExpenses;
private BigDecimal totalBankExpenses;
@Column(name = "total_cash_expenses")
private Double totalCashExpenses;
private BigDecimal totalCashExpenses;
@Column(name = "total_bank_incomes")
private Double totalBankIncomes;
private BigDecimal totalBankIncomes;
@Column(name = "total_cash_incomes")
private Double totalCashIncomes;
private BigDecimal totalCashIncomes;
@Column(name = "created_at")
private Instant createdAt;
public Long getId() {
public Byte getId() {
return id;
}
public Double getInitialBank() {
public BigDecimal getInitialBank() {
return initialBank;
}
public Double getInitialCash() {
public BigDecimal getInitialCash() {
return initialCash;
}
public Double getTotalBankExpenses() {
public BigDecimal getTotalBankExpenses() {
return totalBankExpenses;
}
public Double getTotalCashExpenses() {
public BigDecimal getTotalCashExpenses() {
return totalCashExpenses;
}
public Double getTotalBankIncomes() {
public BigDecimal getTotalBankIncomes() {
return totalBankIncomes;
}
public Double getTotalCashIncomes() {
public BigDecimal getTotalCashIncomes() {
return totalCashIncomes;
}

View File

@@ -9,11 +9,11 @@ import java.util.UUID;
@Entity
@Immutable
@Table(name = "v_huertos_member")
public class VHuertosMember {
@Table(name = "v_huertos_members")
public class VHuertosMembers {
@Id
@Column(name = "user_id")
@Column(name = "user_id", columnDefinition = "BINARY(16)")
private byte[] userIdBin;
@Transient

View File

@@ -4,6 +4,7 @@ import jakarta.persistence.*;
import net.miarma.backlib.util.UuidUtil;
import org.hibernate.annotations.Immutable;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.UUID;
@@ -13,13 +14,13 @@ import java.util.UUID;
public class VIncomesWithFullNames {
@Id
@Column(name = "income_id")
@Column(name = "income_id", columnDefinition = "BINARY(16)")
private byte[] incomeIdBin;
@Transient
private UUID incomeId;
@Column(name = "user_id")
@Column(name = "user_id", columnDefinition = "BINARY(16)")
private byte[] userIdBin;
@Transient
@@ -29,7 +30,7 @@ public class VIncomesWithFullNames {
private String displayName;
private String concept;
private Double amount;
private BigDecimal amount;
private Byte type;
private Byte frequency;
@@ -63,7 +64,7 @@ public class VIncomesWithFullNames {
return concept;
}
public Double getAmount() {
public BigDecimal getAmount() {
return amount;
}

View File

@@ -13,7 +13,7 @@ import java.util.UUID;
public class VRequestsWithPreUsers {
@Id
@Column(name = "request_id")
@Column(name = "request_id", columnDefinition = "BINARY(16)")
private byte[] requestIdBin;
@Transient
@@ -25,7 +25,7 @@ public class VRequestsWithPreUsers {
@Column(name = "request_status")
private Byte requestStatus;
@Column(name = "requested_by")
@Column(name = "requested_by", columnDefinition = "BINARY(16)")
private byte[] requestedByBin;
@Transient
@@ -34,7 +34,7 @@ public class VRequestsWithPreUsers {
@Column(name = "requested_by_name")
private String requestedByName;
@Column(name = "target_user_id")
@Column(name = "target_user_id", columnDefinition = "BINARY(16)")
private byte[] targetUserIdBin;
@Transient
@@ -44,7 +44,7 @@ public class VRequestsWithPreUsers {
private Instant requestCreatedAt;
// --- PreUser ---
@Column(name = "pre_user_id")
@Column(name = "pre_user_id", columnDefinition = "BINARY(16)")
private byte[] preUserIdBin;
@Transient

View File

@@ -3,5 +3,4 @@ package net.miarma.backend.huertos.repository;
import net.miarma.backend.huertos.model.Expense;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ExpenseRepository extends JpaRepository<Expense, byte[]> {
}
public interface ExpenseRepository extends JpaRepository<Expense, byte[]> {}

View File

@@ -3,5 +3,8 @@ package net.miarma.backend.huertos.repository;
import net.miarma.backend.huertos.model.HuertosUserMetadata;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface HuertosUserMetadataRepository extends JpaRepository<HuertosUserMetadata, byte[]> {
Optional<HuertosUserMetadata> findByMemberNumber(Integer memberNumber);
}

View File

@@ -1,12 +0,0 @@
package net.miarma.backend.huertos.repository.view;
import net.miarma.backend.huertos.model.view.VHuertosMember;
import org.springframework.data.repository.Repository;
import java.util.List;
import java.util.Optional;
public interface VHuertosMemberRepository extends Repository<VHuertosMember, byte[]> {
List<VHuertosMember> findAll();
Optional<VHuertosMember> findById(byte[] userId);
}

View File

@@ -0,0 +1,12 @@
package net.miarma.backend.huertos.repository.view;
import net.miarma.backend.huertos.model.view.VHuertosMembers;
import org.springframework.data.repository.Repository;
import java.util.List;
import java.util.Optional;
public interface VHuertosMembersRepository extends Repository<VHuertosMembers, byte[]> {
List<VHuertosMembers> findAll();
Optional<VHuertosMembers> findById(byte[] userId);
}

View File

@@ -4,8 +4,8 @@ import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.miarma.backend.huertos.model.view.VHuertosMember;
import net.miarma.backend.huertos.service.view.VHuertosMemberService;
import net.miarma.backend.huertos.model.view.VHuertosMembers;
import net.miarma.backend.huertos.service.view.VHuertosMembersService;
import net.miarma.backlib.security.JwtService;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
@@ -19,9 +19,9 @@ import java.util.UUID;
public class HuertosJwtFilter extends OncePerRequestFilter {
private final JwtService jwtService;
private final VHuertosMemberService huertosUserService;
private final VHuertosMembersService huertosUserService;
public HuertosJwtFilter(JwtService jwtService, VHuertosMemberService huertosUserService) {
public HuertosJwtFilter(JwtService jwtService, VHuertosMembersService huertosUserService) {
this.jwtService = jwtService;
this.huertosUserService = huertosUserService;
}
@@ -37,18 +37,20 @@ public class HuertosJwtFilter extends OncePerRequestFilter {
if (jwtService.validateToken(token)) {
UUID userId = jwtService.getUserId(token);
Byte serviceId = jwtService.getServiceId(token);
VHuertosMember huertosUser = huertosUserService.getById(userId);
VHuertosMembers huertosUser = huertosUserService.getById(userId);
if (huertosUser != null) {
var principal = new HuertosPrincipal(
userId,
huertosUser.getRole(),
huertosUser.getType()
userId,
huertosUser.getRole(),
huertosUser.getType(),
serviceId
);
var auth = new UsernamePasswordAuthenticationToken(
principal, null, principal.getAuthorities()
principal, null, principal.getAuthorities()
);
SecurityContextHolder.getContext().setAuthentication(auth);

View File

@@ -14,16 +14,19 @@ public class HuertosPrincipal implements UserDetails {
private final UUID userId;
private final Byte role;
private final Byte type;
private final Byte serviceId;
public HuertosPrincipal(UUID userId, Byte role, Byte type) {
public HuertosPrincipal(UUID userId, Byte role, Byte type, Byte serviceId) {
this.userId = userId;
this.role = role;
this.type = type;
this.serviceId = serviceId;
}
public UUID getUserId() { return userId; }
public Byte getHuertosRole() { return role; }
public Byte getHuertosType() { return type; }
public Byte getServiceId() { return serviceId; }
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {

View File

@@ -0,0 +1,36 @@
package net.miarma.backend.huertos.security;
/**
* Clase de utilidad para censurar nombres.
* Censura los nombres dejando las primeras 3 letras visibles y el resto con asteriscos.
* Si el nombre es muy largo, lo acorta a 16 caracteres y añade "..." al final.
* @author José Manuel Amador Gallardo
*/
public class NameCensorer {
public static String censor(String name) {
if (name == null || name.isBlank()) return "";
String[] words = name.trim().split("\\s+");
for (int i = 0; i < words.length; i++) {
String word = words[i];
int len = word.length();
if (len > 3) {
words[i] = word.substring(0, 3) + "*".repeat(len - 3);
} else if (len > 0) {
words[i] = word.charAt(0) + "*".repeat(len - 1);
}
}
String censored = String.join(" ", words);
if (censored.length() > 16) {
censored = censored.substring(0, 16) + "...";
}
return censored;
}
}

View File

@@ -1,6 +1,7 @@
package net.miarma.backend.huertos.service;
import jakarta.transaction.Transactional;
import net.miarma.backend.huertos.dto.AnnouncementDto;
import net.miarma.backend.huertos.model.Announcement;
import net.miarma.backend.huertos.repository.AnnouncementRepository;
import net.miarma.backlib.util.UuidUtil;
@@ -38,7 +39,7 @@ public class AnnouncementService {
return announcementRepository.save(announcement);
}
public Announcement update(UUID announceId, Announcement dto) {
public Announcement update(UUID announceId, AnnouncementDto.Request dto) {
byte[] idBytes = UuidUtil.uuidToBin(announceId);
Announcement announcement = announcementRepository.findById(idBytes)
.orElseThrow(() -> new RuntimeException("Announcement not found"));

View File

@@ -1,6 +1,7 @@
package net.miarma.backend.huertos.service;
import jakarta.transaction.Transactional;
import net.miarma.backend.huertos.dto.ExpenseDto;
import net.miarma.backend.huertos.model.Expense;
import net.miarma.backend.huertos.repository.ExpenseRepository;
import net.miarma.backlib.util.UuidUtil;
@@ -50,7 +51,7 @@ public class ExpenseService {
return expenseRepository.save(expense);
}
public Expense update(UUID expenseId, Expense dto) {
public Expense update(UUID expenseId, ExpenseDto.Request dto) {
byte[] idBytes = UuidUtil.uuidToBin(expenseId);
Expense expense = expenseRepository.findById(idBytes)
.orElseThrow(() -> new RuntimeException("Expense not found"));

View File

@@ -3,6 +3,7 @@ package net.miarma.backend.huertos.service;
import java.time.Instant;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
@@ -10,6 +11,8 @@ import jakarta.transaction.Transactional;
import net.miarma.backend.huertos.model.HuertosUserMetadata;
import net.miarma.backend.huertos.repository.HuertosUserMetadataRepository;
import net.miarma.backlib.util.UuidUtil;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Service
@Transactional
@@ -28,7 +31,17 @@ public class HuertosUserMetadataService {
public HuertosUserMetadata getById(UUID userId) {
byte[] idBytes = UuidUtil.uuidToBin(userId);
return repository.findById(idBytes)
.orElseThrow(() -> new RuntimeException("User metadata not found"));
.orElseThrow(() -> new RuntimeException("User metadata not found"));
}
public HuertosUserMetadata getByMemberNumber(Integer memberNumber) {
return repository.findByMemberNumber(memberNumber)
.orElseThrow(() -> new RuntimeException("User metadata not found"));
}
public boolean existsById(UUID userId) {
byte[] idBytes = UuidUtil.uuidToBin(userId);
return repository.existsById(idBytes);
}
public HuertosUserMetadata create(HuertosUserMetadata meta) {
@@ -79,4 +92,16 @@ public class HuertosUserMetadataService {
}
repository.deleteById(idBytes);
}
public Integer getLatestMemberNumber() {
return repository.findAll()
.stream()
.map(HuertosUserMetadata::getMemberNumber)
.max(Integer::compareTo)
.get();
}
public Boolean existsByMemberNumber(Integer memberNumber) {
return getByMemberNumber(memberNumber).getUserId() != null;
}
}

View File

@@ -4,6 +4,9 @@ import java.time.Instant;
import java.util.List;
import java.util.UUID;
import net.miarma.backend.huertos.dto.IncomeDto;
import net.miarma.backend.huertos.model.HuertosUserMetadata;
import net.miarma.backend.huertos.repository.HuertosUserMetadataRepository;
import org.springframework.stereotype.Service;
import jakarta.transaction.Transactional;
@@ -15,25 +18,27 @@ import net.miarma.backlib.util.UuidUtil;
@Transactional
public class IncomeService {
private final IncomeRepository repository;
private final IncomeRepository incomeRepository;
private final HuertosUserMetadataService metadataService;
public IncomeService(IncomeRepository repository) {
this.repository = repository;
public IncomeService(IncomeRepository incomeRepository,
HuertosUserMetadataService metadataService) {
this.incomeRepository = incomeRepository;
this.metadataService = metadataService;
}
public List<Income> getAll() {
return repository.findAll();
return incomeRepository.findAll();
}
public Income getById(UUID incomeId) {
byte[] idBytes = UuidUtil.uuidToBin(incomeId);
return repository.findById(idBytes)
return incomeRepository.findById(idBytes)
.orElseThrow(() -> new RuntimeException("Income not found"));
}
public List<Income> getByUserId(UUID userId) {
byte[] idBytes = UuidUtil.uuidToBin(userId);
return repository.findAll().stream()
return incomeRepository.findAll().stream()
.filter(i -> i.getUserId().equals(userId))
.toList();
}
@@ -52,13 +57,13 @@ public class IncomeService {
income.setIncomeId(UUID.randomUUID());
income.setCreatedAt(Instant.now());
return repository.save(income);
return incomeRepository.save(income);
}
public Income update(UUID incomeId, Income dto) {
public Income update(UUID incomeId, IncomeDto.Request dto) {
byte[] idBytes = UuidUtil.uuidToBin(incomeId);
Income income = repository.findById(idBytes)
Income income = incomeRepository.findById(idBytes)
.orElseThrow(() -> new RuntimeException("Income not found"));
if (dto.getConcept() != null) income.setConcept(dto.getConcept());
@@ -71,16 +76,31 @@ public class IncomeService {
if (dto.getType() != null) income.setType(dto.getType());
if (dto.getFrequency() != null) income.setFrequency(dto.getFrequency());
return repository.save(income);
return incomeRepository.save(income);
}
public void delete(UUID incomeId) {
byte[] idBytes = UuidUtil.uuidToBin(incomeId);
if (!repository.existsById(idBytes)) {
if (!incomeRepository.existsById(idBytes)) {
throw new RuntimeException("Income not found");
}
repository.deleteById(idBytes);
incomeRepository.deleteById(idBytes);
}
public Boolean existsByMemberNumber(Integer memberNumber) {
try {
UUID userId = metadataService.getByMemberNumber(memberNumber).getUserId();
return !getByUserId(userId).isEmpty();
} catch (RuntimeException e) {
return false;
}
}
public Boolean hasPaid(Integer memberNumber) {
UUID userId = metadataService.getByMemberNumber(memberNumber).getUserId();
List<Income> incomes = getByUserId(userId);
return !incomes.isEmpty() && incomes.stream().allMatch(Income::isPaid);
}
}

View File

@@ -0,0 +1,148 @@
package net.miarma.backend.huertos.service;
import net.miarma.backend.huertos.client.HuertosWebClient;
import net.miarma.backend.huertos.dto.MemberDto;
import net.miarma.backend.huertos.dto.WaitlistCensoredDto;
import net.miarma.backend.huertos.mapper.HuertosUserMetadataMapper;
import net.miarma.backend.huertos.security.NameCensorer;
import net.miarma.backlib.dto.UserWithCredentialDto;
import org.springframework.stereotype.Service;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;
import java.util.function.Predicate;
@Service
public class MemberService {
private final HuertosWebClient huertosWebClient;
private final IncomeService incomeService;
private final RequestService requestService;
private final HuertosUserMetadataService metadataService;
public MemberService(HuertosWebClient huertosWebClient,
IncomeService incomeService,
RequestService requestService,
HuertosUserMetadataService metadataService) {
this.huertosWebClient = huertosWebClient;
this.incomeService = incomeService;
this.requestService = requestService;
this.metadataService = metadataService;
}
public MemberDto getById(UUID userId, Byte serviceId) {
var uwc = huertosWebClient.getUserWithCredential(userId, serviceId);
var meta = metadataService.getById(userId);
return new MemberDto(uwc.user(), uwc.account(),
HuertosUserMetadataMapper.toDto(meta));
}
public List<MemberDto> getAll(Byte serviceId) {
List<UserWithCredentialDto> all = huertosWebClient.getAllUsersWithCredentials(serviceId);
return all.stream()
.filter(uwc -> metadataService.existsById(uwc.user().getUserId()))
.map(uwc -> {
var meta = metadataService.getById(uwc.user().getUserId());
return new MemberDto(
uwc.user(),
uwc.account(),
HuertosUserMetadataMapper.toDto(meta)
);
})
.toList();
}
public Integer getLatestMemberNumber() {
return metadataService.getLatestMemberNumber();
}
public List<MemberDto> getWaitlist() {
List<UserWithCredentialDto> all = huertosWebClient.getAllUsersWithCredentials((byte)1);
return all.stream()
.filter(uwc -> metadataService.existsById(uwc.user().getUserId()))
.filter(uwc -> uwc.account().getStatus() != 0)
.map(uwc -> {
var meta = metadataService.getById(uwc.user().getUserId());
return new MemberDto(uwc.user(), uwc.account(),
HuertosUserMetadataMapper.toDto(meta));
})
.filter(dto -> dto.metadata().getType().equals((byte) 0))
.sorted(Comparator.comparing(dto -> dto.metadata().getCreatedAt()))
.toList();
}
public List<WaitlistCensoredDto> getWaitlistLimited() {
return getWaitlist().stream()
.map(dto -> {
WaitlistCensoredDto censored = new WaitlistCensoredDto();
censored.setName(NameCensorer.censor(dto.user().getDisplayName()));
return censored;
})
.toList();
}
public MemberDto getByMemberNumber(Integer memberNumber) {
return getAll((byte)1).stream()
.filter(dto -> dto.metadata().getMemberNumber().equals(memberNumber))
.findFirst()
.orElseThrow(() -> new RuntimeException("Member not found"));
}
public MemberDto getByPlotNumber(Integer plotNumber) {
return getAll((byte)1).stream()
.filter(dto -> dto.metadata().getPlotNumber().equals(plotNumber))
.findFirst()
.orElseThrow(() -> new RuntimeException("Member not found"));
}
public MemberDto getByDni(String dni) {
return getAll((byte)1).stream()
.filter(dto -> dni.equals(dto.metadata().getDni()))
.findFirst()
.orElseThrow(() -> new RuntimeException("Member not found"));
}
public Boolean hasIncomes(Integer memberNumber) {
return incomeService.existsByMemberNumber(memberNumber);
}
public Boolean hasPaid(Integer memberNumber) {
return incomeService.hasPaid(memberNumber);
}
public Boolean hasCollaborator(Integer memberNumber) {
List<MemberDto> all = getAll((byte)1);
var member = all.stream()
.filter(dto -> dto.metadata().getMemberNumber().equals(memberNumber))
.findFirst()
.orElse(null);
if (member == null) return false;
Integer plotNumber = member.metadata().getPlotNumber();
if (plotNumber == null) return false;
List<MemberDto> plotMembers = all.stream()
.filter(dto -> plotNumber.equals(dto.metadata().getPlotNumber()))
.toList();
return plotMembers.stream()
.anyMatch(dto -> dto.metadata().getType().equals((byte)2));
}
public Boolean hasGreenhouse(Integer memberNumber) {
return metadataService.existsByMemberNumber(memberNumber);
}
public Boolean hasCollaboratorRequest(Integer memberNumber) {
return requestService.existsCollaboratorRequest(memberNumber);
}
public Boolean hasGreenhouseRequest(Integer memberNumber) {
return requestService.existsGreenhouseRequest(memberNumber);
}
}

View File

@@ -4,6 +4,7 @@ import java.time.Instant;
import java.util.List;
import java.util.UUID;
import net.miarma.backend.huertos.dto.PreUserDto;
import org.springframework.stereotype.Service;
import jakarta.transaction.Transactional;
@@ -65,7 +66,7 @@ public class PreUserService {
return repository.save(preUser);
}
public PreUser update(UUID preUserId, PreUser dto) {
public PreUser update(UUID preUserId, PreUserDto.Request dto) {
byte[] idBytes = UuidUtil.uuidToBin(preUserId);
PreUser preUser = repository.findById(idBytes)

View File

@@ -4,6 +4,8 @@ import java.time.Instant;
import java.util.List;
import java.util.UUID;
import net.miarma.backend.huertos.dto.RequestDto;
import net.miarma.backend.huertos.model.HuertosUserMetadata;
import org.springframework.stereotype.Service;
import jakarta.transaction.Transactional;
@@ -15,30 +17,39 @@ import net.miarma.backlib.util.UuidUtil;
@Transactional
public class RequestService {
private final RequestRepository repository;
private final RequestRepository requestRepository;
private final HuertosUserMetadataService metadataService;
public RequestService(RequestRepository repository) {
this.repository = repository;
public RequestService(RequestRepository requestRepository,
HuertosUserMetadataService metadataSertice) {
this.requestRepository = requestRepository;
this.metadataService = metadataSertice;
}
public List<Request> getAll() {
return repository.findAll();
return requestRepository.findAll();
}
public Request getById(UUID requestId) {
byte[] idBytes = UuidUtil.uuidToBin(requestId);
return repository.findById(idBytes)
return requestRepository.findById(idBytes)
.orElseThrow(() -> new RuntimeException("Request not found"));
}
public List<Request> getByUserId(UUID userId) {
return requestRepository.findAll().stream()
.filter(r -> r.getRequestedBy().equals(userId))
.toList();
}
public List<Request> getByRequestedBy(UUID requestedBy) {
return repository.findAll().stream()
return requestRepository.findAll().stream()
.filter(r -> r.getRequestedBy() != null && r.getRequestedBy().equals(requestedBy))
.toList();
}
public List<Request> getByTargetUserId(UUID targetUserId) {
return repository.findAll().stream()
return requestRepository.findAll().stream()
.filter(r -> r.getTargetUserId() != null && r.getTargetUserId().equals(targetUserId))
.toList();
}
@@ -57,13 +68,13 @@ public class RequestService {
request.setRequestId(UUID.randomUUID());
request.setCreatedAt(Instant.now());
return repository.save(request);
return requestRepository.save(request);
}
public Request update(UUID requestId, Request dto) {
public Request update(UUID requestId, RequestDto.Request dto) {
byte[] idBytes = UuidUtil.uuidToBin(requestId);
Request request = repository.findById(idBytes)
Request request = requestRepository.findById(idBytes)
.orElseThrow(() -> new RuntimeException("Request not found"));
if (dto.getType() != null) request.setType(dto.getType());
@@ -71,16 +82,46 @@ public class RequestService {
if (dto.getRequestedBy() != null) request.setRequestedBy(dto.getRequestedBy());
if (dto.getTargetUserId() != null) request.setTargetUserId(dto.getTargetUserId());
return repository.save(request);
return requestRepository.save(request);
}
public Request acceptRequest(UUID requestId) {
byte[] idBytes = UuidUtil.uuidToBin(requestId);
Request request = requestRepository.findById(idBytes)
.orElseThrow(() -> new RuntimeException("Request not found"));
request.setStatus((byte)1);
return requestRepository.save(request);
}
public Request rejectRequest(UUID requestId) {
byte[] idBytes = UuidUtil.uuidToBin(requestId);
Request request = requestRepository.findById(idBytes)
.orElseThrow(() -> new RuntimeException("Request not found"));
request.setStatus((byte)2);
return requestRepository.save(request);
}
public void delete(UUID requestId) {
byte[] idBytes = UuidUtil.uuidToBin(requestId);
if (!repository.existsById(idBytes)) {
if (!requestRepository.existsById(idBytes)) {
throw new RuntimeException("Request not found");
}
repository.deleteById(idBytes);
requestRepository.deleteById(idBytes);
}
public Boolean existsCollaboratorRequest(Integer memberNumber) {
UUID userId = metadataService.getByMemberNumber(memberNumber).getUserId();
return getByUserId(userId).stream()
.anyMatch(r -> r.getType().equals((byte)2) ||
r.getType().equals((byte)3));
}
public Boolean existsGreenhouseRequest(Integer memberNumber) {
UUID userId = metadataService.getByMemberNumber(memberNumber).getUserId();
return getByUserId(userId).stream()
.anyMatch(r -> r.getType().equals((byte)4) ||
r.getType().equals((byte)5));
}
}

View File

@@ -6,25 +6,25 @@ import java.util.UUID;
import org.springframework.stereotype.Service;
import jakarta.transaction.Transactional;
import net.miarma.backend.huertos.model.view.VHuertosMember;
import net.miarma.backend.huertos.repository.view.VHuertosMemberRepository;
import net.miarma.backend.huertos.model.view.VHuertosMembers;
import net.miarma.backend.huertos.repository.view.VHuertosMembersRepository;
import net.miarma.backlib.util.UuidUtil;
@Service
@Transactional
public class VHuertosMemberService {
public class VHuertosMembersService {
private final VHuertosMemberRepository repository;
private final VHuertosMembersRepository repository;
public VHuertosMemberService(VHuertosMemberRepository repository) {
public VHuertosMembersService(VHuertosMembersRepository repository) {
this.repository = repository;
}
public List<VHuertosMember> getAll() {
public List<VHuertosMembers> getAll() {
return repository.findAll();
}
public VHuertosMember getById(UUID userId) {
public VHuertosMembers getById(UUID userId) {
byte[] idBytes = UuidUtil.uuidToBin(userId);
return repository.findById(idBytes)
.orElseThrow(() -> new RuntimeException("Member not found"));

View File

@@ -16,7 +16,7 @@ spring:
jpa:
open-in-view: false
hibernate:
ddl-auto: update
ddl-auto: validate
properties:
hibernate:
format_sql: true
@@ -35,6 +35,32 @@ logging:
org.hibernate.orm.jdbc.bind: TRACE
org.springframework.security: INFO
jwt:
private-key-path: /home/jomaa/.config/miarma-backend/private.pem
public-key-path: /home/jomaa/.config/miarma-backend/public.pem
expiration-ms: 3600000
core:
url: "http://localhost:8080/v2/core"
huertos:
user: "SYSTEM"
password: "mR193*8ztxgskTrt"
mail:
smtp:
server: smtp.dondominio.com
port: 587
password:
presidente: Huertos$Presidenzia2025
secretaria: Huertos$Secretarya2025
tesoreria: Huertos$Tesorerya2025
admin: Mi.Primer.Aire.Mundo.Clima;99
noreply: hpeeuRqn2-2MN_
imap:
server: imap.dondominio.com
port: 993
management:
endpoints:
web: