1
0
Fork 0
mirror of https://github.com/s-frick/effigenix.git synced 2026-03-28 15:59:35 +01:00

feat(loadtest): Gatling-Lasttests mit ~2500 Requests für komprimiertes Jahres-Volumen

Szenarien: Stammdaten-CRUD, Produktions-Workflow, Lagerverwaltung,
Read-Only-Zugriffe. Batch-Repository auf Summary-Projektion umgestellt,
Permissions-Changeset Merge-Konflikt aufgelöst, Unit-Enum im
JsonBodyBuilder korrigiert (KILOGRAM → KG).
This commit is contained in:
Sebastian Frick 2026-02-24 21:44:16 +01:00
parent 8a9bf849a9
commit 11fb62383b
21 changed files with 1856 additions and 38 deletions

View file

@ -28,7 +28,7 @@ public class ListBatches {
return Result.failure(new BatchError.Unauthorized("Not authorized to read batches"));
}
switch (batchRepository.findAll()) {
switch (batchRepository.findAllSummary()) {
case Result.Failure(var err) ->
{ return Result.failure(new BatchError.RepositoryFailure(err.message())); }
case Result.Success(var batches) ->
@ -41,7 +41,7 @@ public class ListBatches {
return Result.failure(new BatchError.Unauthorized("Not authorized to read batches"));
}
switch (batchRepository.findByStatus(status)) {
switch (batchRepository.findByStatusSummary(status)) {
case Result.Failure(var err) ->
{ return Result.failure(new BatchError.RepositoryFailure(err.message())); }
case Result.Success(var batches) ->
@ -54,7 +54,7 @@ public class ListBatches {
return Result.failure(new BatchError.Unauthorized("Not authorized to read batches"));
}
switch (batchRepository.findByProductionDate(date)) {
switch (batchRepository.findByProductionDateSummary(date)) {
case Result.Failure(var err) ->
{ return Result.failure(new BatchError.RepositoryFailure(err.message())); }
case Result.Success(var batches) ->
@ -75,7 +75,7 @@ public class ListBatches {
return Result.success(List.of());
}
List<RecipeId> recipeIds = recipes.stream().map(Recipe::id).toList();
switch (batchRepository.findByRecipeIds(recipeIds)) {
switch (batchRepository.findByRecipeIdsSummary(recipeIds)) {
case Result.Failure(var batchErr) ->
{ return Result.failure(new BatchError.RepositoryFailure(batchErr.message())); }
case Result.Success(var batches) ->

View file

@ -21,5 +21,13 @@ public interface BatchRepository {
Result<RepositoryError, List<Batch>> findByRecipeIds(List<RecipeId> recipeIds);
Result<RepositoryError, List<Batch>> findAllSummary();
Result<RepositoryError, List<Batch>> findByStatusSummary(BatchStatus status);
Result<RepositoryError, List<Batch>> findByProductionDateSummary(LocalDate date);
Result<RepositoryError, List<Batch>> findByRecipeIdsSummary(List<RecipeId> recipeIds);
Result<RepositoryError, Void> save(Batch batch);
}

View file

@ -3,11 +3,14 @@ package de.effigenix.infrastructure.masterdata.persistence.repository;
import de.effigenix.infrastructure.masterdata.persistence.entity.FrameContractEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface FrameContractJpaRepository extends JpaRepository<FrameContractEntity, String> {
Optional<FrameContractEntity> findByCustomerId(String customerId);
List<FrameContractEntity> findByCustomerIdIn(List<String> customerIds);
void deleteByCustomerId(String customerId);
}

View file

@ -1,6 +1,7 @@
package de.effigenix.infrastructure.masterdata.persistence.repository;
import de.effigenix.domain.masterdata.*;
import de.effigenix.infrastructure.masterdata.persistence.entity.CustomerEntity;
import de.effigenix.infrastructure.masterdata.persistence.entity.FrameContractEntity;
import de.effigenix.infrastructure.masterdata.persistence.mapper.CustomerMapper;
import de.effigenix.shared.common.RepositoryError;
@ -12,7 +13,9 @@ import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
@Repository
@ -52,13 +55,7 @@ public class JpaCustomerRepository implements CustomerRepository {
@Override
public Result<RepositoryError, List<Customer>> findAll() {
try {
List<Customer> result = jpaRepository.findAll().stream()
.map(entity -> {
var fc = frameContractJpaRepository.findByCustomerId(entity.getId()).orElse(null);
return mapper.toDomain(entity, fc);
})
.collect(Collectors.toList());
return Result.success(result);
return Result.success(mapWithFrameContracts(jpaRepository.findAll()));
} catch (Exception e) {
logger.trace("Database error in findAll", e);
return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
@ -68,13 +65,7 @@ public class JpaCustomerRepository implements CustomerRepository {
@Override
public Result<RepositoryError, List<Customer>> findByType(CustomerType type) {
try {
List<Customer> result = jpaRepository.findByType(type.name()).stream()
.map(entity -> {
var fc = frameContractJpaRepository.findByCustomerId(entity.getId()).orElse(null);
return mapper.toDomain(entity, fc);
})
.collect(Collectors.toList());
return Result.success(result);
return Result.success(mapWithFrameContracts(jpaRepository.findByType(type.name())));
} catch (Exception e) {
logger.trace("Database error in findByType", e);
return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
@ -84,19 +75,24 @@ public class JpaCustomerRepository implements CustomerRepository {
@Override
public Result<RepositoryError, List<Customer>> findByStatus(CustomerStatus status) {
try {
List<Customer> result = jpaRepository.findByStatus(status.name()).stream()
.map(entity -> {
var fc = frameContractJpaRepository.findByCustomerId(entity.getId()).orElse(null);
return mapper.toDomain(entity, fc);
})
.collect(Collectors.toList());
return Result.success(result);
return Result.success(mapWithFrameContracts(jpaRepository.findByStatus(status.name())));
} catch (Exception e) {
logger.trace("Database error in findByStatus", e);
return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
}
}
private List<Customer> mapWithFrameContracts(List<CustomerEntity> entities) {
List<String> customerIds = entities.stream()
.map(CustomerEntity::getId)
.toList();
Map<String, FrameContractEntity> fcMap = frameContractJpaRepository.findByCustomerIdIn(customerIds).stream()
.collect(Collectors.toMap(FrameContractEntity::getCustomerId, Function.identity()));
return entities.stream()
.map(entity -> mapper.toDomain(entity, fcMap.get(entity.getId())))
.collect(Collectors.toList());
}
@Override
@Transactional
public Result<RepositoryError, Void> save(Customer customer) {

View file

@ -128,6 +128,38 @@ public class BatchMapper {
);
}
public Batch toDomainSummary(BatchEntity entity) {
Quantity actualQuantity = entity.getActualQuantityAmount() != null
? Quantity.reconstitute(entity.getActualQuantityAmount(), UnitOfMeasure.valueOf(entity.getActualQuantityUnit()))
: null;
Quantity waste = entity.getWasteAmount() != null
? Quantity.reconstitute(entity.getWasteAmount(), UnitOfMeasure.valueOf(entity.getWasteUnit()))
: null;
return Batch.reconstitute(
BatchId.of(entity.getId()),
new BatchNumber(entity.getBatchNumber()),
RecipeId.of(entity.getRecipeId()),
BatchStatus.valueOf(entity.getStatus()),
Quantity.reconstitute(
entity.getPlannedQuantityAmount(),
UnitOfMeasure.valueOf(entity.getPlannedQuantityUnit())
),
actualQuantity,
waste,
entity.getRemarks(),
entity.getProductionDate(),
entity.getBestBeforeDate(),
entity.getCreatedAt(),
entity.getUpdatedAt(),
entity.getCompletedAt(),
entity.getCancellationReason(),
entity.getCancelledAt(),
entity.getVersion(),
List.of()
);
}
private ConsumptionEntity toConsumptionEntity(Consumption c, BatchEntity parent) {
return new ConsumptionEntity(
c.id().value(),

View file

@ -108,6 +108,59 @@ public class JpaBatchRepository implements BatchRepository {
}
}
@Override
public Result<RepositoryError, List<Batch>> findAllSummary() {
try {
List<Batch> result = jpaRepository.findAll().stream()
.map(mapper::toDomainSummary)
.collect(Collectors.toList());
return Result.success(result);
} catch (Exception e) {
logger.trace("Database error in findAllSummary", e);
return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
}
}
@Override
public Result<RepositoryError, List<Batch>> findByStatusSummary(BatchStatus status) {
try {
List<Batch> result = jpaRepository.findByStatus(status.name()).stream()
.map(mapper::toDomainSummary)
.collect(Collectors.toList());
return Result.success(result);
} catch (Exception e) {
logger.trace("Database error in findByStatusSummary", e);
return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
}
}
@Override
public Result<RepositoryError, List<Batch>> findByProductionDateSummary(LocalDate date) {
try {
List<Batch> result = jpaRepository.findByProductionDate(date).stream()
.map(mapper::toDomainSummary)
.collect(Collectors.toList());
return Result.success(result);
} catch (Exception e) {
logger.trace("Database error in findByProductionDateSummary", e);
return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
}
}
@Override
public Result<RepositoryError, List<Batch>> findByRecipeIdsSummary(List<RecipeId> recipeIds) {
try {
List<String> ids = recipeIds.stream().map(RecipeId::value).toList();
List<Batch> result = jpaRepository.findByRecipeIdIn(ids).stream()
.map(mapper::toDomainSummary)
.collect(Collectors.toList());
return Result.success(result);
} catch (Exception e) {
logger.trace("Database error in findByRecipeIdsSummary", e);
return Result.failure(new RepositoryError.DatabaseError(e.getMessage()));
}
}
@Override
@Transactional
public Result<RepositoryError, Void> save(Batch batch) {

View file

@ -6,7 +6,16 @@
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet id="025-seed-production-order-permissions" author="effigenix">
<comment>Add PRODUCTION_ORDER_READ and PRODUCTION_ORDER_WRITE permissions for ADMIN and PRODUCTION_MANAGER roles (idempotent)</comment>
<preConditions onFail="MARK_RAN">
<not>
<sqlCheck expectedResult="1">
SELECT COUNT(*) FROM role_permissions
WHERE role_id = 'c0a80121-0000-0000-0000-000000000001'
AND permission = 'PRODUCTION_ORDER_READ'
</sqlCheck>
</not>
</preConditions>
<comment>Add PRODUCTION_ORDER_READ and PRODUCTION_ORDER_WRITE permissions for ADMIN and PRODUCTION_MANAGER roles (skipped if already present from 002)</comment>
<sql>
INSERT INTO role_permissions (role_id, permission) VALUES