AccountQueryController.java

package com.github.jenkaby.bikerental.finance.web.query;

import com.github.jenkaby.bikerental.finance.application.usecase.GetCustomerAccountBalancesUseCase;
import com.github.jenkaby.bikerental.finance.application.usecase.GetTransactionHistoryUseCase;
import com.github.jenkaby.bikerental.finance.domain.model.TransactionHistoryFilter;
import com.github.jenkaby.bikerental.finance.web.query.dto.CustomerAccountBalancesResponse;
import com.github.jenkaby.bikerental.finance.web.query.dto.TransactionHistoryFilterParams;
import com.github.jenkaby.bikerental.finance.web.query.dto.TransactionResponse;
import com.github.jenkaby.bikerental.finance.web.query.mapper.AccountQueryMapper;
import com.github.jenkaby.bikerental.finance.web.query.mapper.TransactionHistoryQueryMapper;
import com.github.jenkaby.bikerental.shared.config.OpenApiConfig;
import com.github.jenkaby.bikerental.shared.domain.model.vo.Page;
import com.github.jenkaby.bikerental.shared.domain.model.vo.PageRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ProblemDetail;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.UUID;

@Validated
@RestController
@RequestMapping("/api/finance/customers")
@Tag(name = OpenApiConfig.Tags.FINANCE)
@Slf4j
public class AccountQueryController {

    private final GetCustomerAccountBalancesUseCase getCustomerAccountBalancesUseCase;
    private final AccountQueryMapper mapper;
    private final GetTransactionHistoryUseCase getTransactionHistoryUseCase;
    private final TransactionHistoryQueryMapper transactionHistoryQueryMapper;

    public AccountQueryController(GetCustomerAccountBalancesUseCase getCustomerAccountBalancesUseCase,
                                  AccountQueryMapper mapper,
                                  GetTransactionHistoryUseCase getTransactionHistoryUseCase,
                                  TransactionHistoryQueryMapper transactionHistoryQueryMapper) {
        this.getCustomerAccountBalancesUseCase = getCustomerAccountBalancesUseCase;
        this.mapper = mapper;
        this.getTransactionHistoryUseCase = getTransactionHistoryUseCase;
        this.transactionHistoryQueryMapper = transactionHistoryQueryMapper;
    }

    @GetMapping("/{customerId}/balances")
    @Operation(summary = "Retrieve customer account balances")
    @ApiResponses({
            @ApiResponse(responseCode = "200", description = "Account balances retrieved",
                    content = @Content(schema = @Schema(implementation = CustomerAccountBalancesResponse.class))),
            @ApiResponse(responseCode = "404", description = "Customer finance account not found",
                    content = @Content(schema = @Schema(implementation = ProblemDetail.class))),
            @ApiResponse(responseCode = "400", description = "Invalid customer UUID format",
                    content = @Content(schema = @Schema(implementation = ProblemDetail.class)))
    })
    public ResponseEntity<CustomerAccountBalancesResponse> getBalances(
            @Parameter(description = "Customer UUID") @PathVariable("customerId") UUID customerId) {
        log.info("[GET] Retrieve balances for customerId={}", customerId);
        var result = getCustomerAccountBalancesUseCase.execute(customerId);
        return ResponseEntity.ok(mapper.toResponse(result));
    }

    @GetMapping("/{customerId}/transactions")
    @Operation(summary = "Retrieve paginated transaction history for a customer")
    @ApiResponses({
            @ApiResponse(responseCode = "200", description = "Transaction history retrieved",
                    content = @Content(schema = @Schema(implementation = Page.class))),
            @ApiResponse(responseCode = "404", description = "Customer finance account not found",
                    content = @Content(schema = @Schema(implementation = ProblemDetail.class))),
            @ApiResponse(responseCode = "400", description = "Invalid request parameters",
                    content = @Content(schema = @Schema(implementation = ProblemDetail.class)))
    })
    public ResponseEntity<Page<TransactionResponse>> getTransactionHistory(
            @Parameter(description = "Customer UUID") @PathVariable("customerId") UUID customerId,
            @ModelAttribute TransactionHistoryFilterParams filterParams,
            @PageableDefault(size = 20) Pageable pageable) {

        log.info("[GET] Transaction history for customerId={} page={} size={} filter={}",
                customerId, pageable.getPageNumber(), pageable.getPageSize(), filterParams);
        var filter = new TransactionHistoryFilter(
                filterParams.fromDate(), filterParams.toDate(),
                filterParams.sourceId(), filterParams.sourceType());
        var pageRequest = new PageRequest(pageable.getPageSize(), pageable.getPageNumber(), null);
        var result = getTransactionHistoryUseCase.execute(customerId, filter, pageRequest);
        return ResponseEntity.ok(result.map(transactionHistoryQueryMapper::toResponse));
    }
}