From 8b57f97d8824838e05880a3612875c826a6171be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20M=C3=BCller?= Date: Wed, 24 Jun 2026 08:33:15 +0200 Subject: [PATCH] fixed Dto mapping in EnergyCommunityAdminController and openapi definition --- .../api/EnergyCommunityAdminController.java | 27 +++--- .../community/api/dto/EnergyCommunityDto.java | 15 ++++ .../backend/community/domain/Membership.java | 1 - .../mapper/EnergyCommunityMapper.java | 16 ++++ .../repository/MembershipRepository.java | 7 ++ .../service/EnergyCommunityService.java | 82 ++++++++++++++----- eeg_frontend/openapi.yaml | 23 +++--- .../admin-energy-community.ts | 8 +- 8 files changed, 130 insertions(+), 49 deletions(-) create mode 100644 eeg_backend/src/main/java/at/mueller/eeg/backend/community/api/dto/EnergyCommunityDto.java create mode 100644 eeg_backend/src/main/java/at/mueller/eeg/backend/community/mapper/EnergyCommunityMapper.java diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/api/EnergyCommunityAdminController.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/api/EnergyCommunityAdminController.java index 99449c1..aeb569f 100644 --- a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/api/EnergyCommunityAdminController.java +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/api/EnergyCommunityAdminController.java @@ -1,6 +1,6 @@ package at.mueller.eeg.backend.community.api; -import at.mueller.eeg.backend.community.domain.EnergyCommunity; +import at.mueller.eeg.backend.community.api.dto.EnergyCommunityDto; import at.mueller.eeg.backend.community.service.EnergyCommunityService; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; @@ -30,32 +30,31 @@ public class EnergyCommunityAdminController { content = @Content( mediaType = MediaType.APPLICATION_JSON_VALUE, array = @ArraySchema( - schema = @Schema(implementation = EnergyCommunity.class) + schema = @Schema(implementation = EnergyCommunityDto.class) ) ) ) - public ResponseEntity> getAllEnergyCommunities() { - return ResponseEntity.ok(service.findAll()); + public ResponseEntity> getAllEnergyCommunities() { + return ResponseEntity.ok(service.getAllCommunitiesForAdmin()); } // GET /api/admin/energy-communities/{id} - @GetMapping("/{id}") - public ResponseEntity getEnergyCommunityById(@PathVariable UUID id) { - return ResponseEntity.ok(service.findById(id)); + @GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity getEnergyCommunityById(@PathVariable UUID id) { + return ResponseEntity.ok(service.getCommunityById(id)); } // POST /api/admin/energy-communities - @PostMapping - public ResponseEntity createEnergyCommunity(@RequestBody EnergyCommunity community) { - EnergyCommunity createdCommunity = service.create(community); - return ResponseEntity.status(HttpStatus.CREATED).body(createdCommunity); + @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity createEnergyCommunity(@RequestBody EnergyCommunityDto community) { + return ResponseEntity.status(HttpStatus.CREATED).body(service.create(community)); } // PUT /api/admin/energy-communities/{id} - @PutMapping("/{id}") - public ResponseEntity updateEnergyCommunity( + @PutMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity updateEnergyCommunity( @PathVariable UUID id, - @RequestBody EnergyCommunity community) { + @RequestBody EnergyCommunityDto community) { return ResponseEntity.ok(service.update(id, community)); } diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/api/dto/EnergyCommunityDto.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/api/dto/EnergyCommunityDto.java new file mode 100644 index 0000000..460974f --- /dev/null +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/api/dto/EnergyCommunityDto.java @@ -0,0 +1,15 @@ +package at.mueller.eeg.backend.community.api.dto; + +import at.mueller.eeg.backend.community.domain.CommunityType; +import java.util.UUID; + +public record EnergyCommunityDto( + UUID id, + String edaEcId, + CommunityType type, + String name, + String gridOperatorId, + String substationId, + String transformerId, + Integer memberCount +) {} \ No newline at end of file diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/domain/Membership.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/domain/Membership.java index ab8d980..c8652dc 100644 --- a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/domain/Membership.java +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/domain/Membership.java @@ -24,7 +24,6 @@ public class Membership { @JoinColumn(name = "energy_community_id", nullable = false) private EnergyCommunity energyCommunity; - // 1 = Lokal, 2 = Regional, 3 = BEG @Column(nullable = false) private Integer priorityLevel; diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/mapper/EnergyCommunityMapper.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/mapper/EnergyCommunityMapper.java new file mode 100644 index 0000000..831b185 --- /dev/null +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/mapper/EnergyCommunityMapper.java @@ -0,0 +1,16 @@ +package at.mueller.eeg.backend.community.mapper; + +import at.mueller.eeg.backend.community.api.dto.EnergyCommunityDto; +import at.mueller.eeg.backend.community.domain.EnergyCommunity; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; + +@Mapper +public interface EnergyCommunityMapper { + + @Mapping(target = "id", ignore = true) + EnergyCommunity toEntity(EnergyCommunityDto dto, @MappingTarget EnergyCommunity entity); + + EnergyCommunityDto toDto(EnergyCommunity entity, int memberCount); +} diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MembershipRepository.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MembershipRepository.java index 94334f9..ad92c73 100644 --- a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MembershipRepository.java +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MembershipRepository.java @@ -3,6 +3,8 @@ package at.mueller.eeg.backend.community.repository; import at.mueller.eeg.backend.community.domain.Membership; import at.mueller.eeg.backend.community.domain.MembershipStatus; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import java.util.List; import java.util.UUID; @@ -18,4 +20,9 @@ public interface MembershipRepository extends JpaRepository { Integer priorityLevel, List statuses ); + + boolean existsByEnergyCommunityId(UUID energyCommunityId); + + @Query("SELECT m.energyCommunity.id, COUNT(m) FROM Membership m WHERE m.energyCommunity.id IN :communityIds AND m.status != 'INACTIVE' GROUP BY m.energyCommunity.id") + List countActiveMembersForCommunities(@Param("communityIds") List communityIds); } \ No newline at end of file diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/service/EnergyCommunityService.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/service/EnergyCommunityService.java index 4c5210c..a96b39e 100644 --- a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/service/EnergyCommunityService.java +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/service/EnergyCommunityService.java @@ -1,53 +1,95 @@ package at.mueller.eeg.backend.community.service; +import at.mueller.eeg.backend.community.api.dto.EnergyCommunityDto; import at.mueller.eeg.backend.community.domain.EnergyCommunity; +import at.mueller.eeg.backend.community.mapper.EnergyCommunityMapper; import at.mueller.eeg.backend.community.repository.EnergyCommunityRepository; +import at.mueller.eeg.backend.community.repository.MembershipRepository; import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.server.ResponseStatusException; import java.util.List; +import java.util.Map; import java.util.UUID; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor public class EnergyCommunityService { - private final EnergyCommunityRepository repository; + private final EnergyCommunityRepository energyCommunityRepository; + private final MembershipRepository membershipRepository; + private final EnergyCommunityMapper energyCommunityMapper; - public List findAll() { - return repository.findAll(); + public List getAllCommunitiesForAdmin() { + var communities = energyCommunityRepository.findAll(); + List communityIds = communities.stream().map(EnergyCommunity::getId).toList(); + + Map memberCountMap = getMemberCountsForIds(communityIds); + + return communities.stream() + .map(ec -> mapToDto(ec, memberCountMap.getOrDefault(ec.getId(), 0))) + .toList(); } - public EnergyCommunity findById(UUID id) { - return repository.findById(id) - .orElseThrow(() -> new IllegalArgumentException("Energy Community mit ID " + id + " nicht gefunden.")); + public EnergyCommunityDto getCommunityById(UUID communityId) { + EnergyCommunity ec = energyCommunityRepository.findById(communityId) + .orElseThrow(() -> new IllegalArgumentException("Energy Community mit ID " + communityId + " nicht gefunden.")); + Map memberCountMap = getMemberCountsForIds(List.of(communityId)); + return mapToDto(ec, memberCountMap.getOrDefault(communityId, 0)); } @Transactional - public EnergyCommunity create(EnergyCommunity community) { - return repository.save(community); + public EnergyCommunityDto create(EnergyCommunityDto community) { + return energyCommunityMapper.toDto( + energyCommunityRepository.save( + energyCommunityMapper.toEntity(community, new EnergyCommunity()) + ), 0); } @Transactional - public EnergyCommunity update(UUID id, EnergyCommunity updatedData) { - EnergyCommunity existing = findById(id); + public EnergyCommunityDto update(UUID id, EnergyCommunityDto dto) { + EnergyCommunity existingEntity = energyCommunityRepository.findById(id) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Energiegemeinschaft nicht gefunden")); + energyCommunityMapper.toEntity(dto, existingEntity); + EnergyCommunity updatedEntity = energyCommunityRepository.save(existingEntity); + Map memberCountMap = getMemberCountsForIds(List.of(id)); - existing.setEdaEcId(updatedData.getEdaEcId()); - existing.setType(updatedData.getType()); - existing.setName(updatedData.getName()); - existing.setGridOperatorId(updatedData.getGridOperatorId()); - existing.setSubstationId(updatedData.getSubstationId()); - existing.setTransformerId(updatedData.getTransformerId()); - - return repository.save(existing); + return energyCommunityMapper.toDto(updatedEntity, memberCountMap.getOrDefault(id, 0)); } @Transactional public void delete(UUID id) { - if (!repository.existsById(id)) { + if (!energyCommunityRepository.existsById(id)) { throw new IllegalArgumentException("Energy Community mit ID " + id + " existiert nicht."); } - repository.deleteById(id); + energyCommunityRepository.deleteById(id); + } + + private Map getMemberCountsForIds(List communityIds) { + if (communityIds.isEmpty()) { + return Map.of(); + } + return membershipRepository.countActiveMembersForCommunities(communityIds).stream() + .collect(Collectors.toMap( + row -> (UUID) row[0], + row -> ((Long) row[1]).intValue() + )); + } + + private EnergyCommunityDto mapToDto(EnergyCommunity ec, int memberCount) { + return new EnergyCommunityDto( + ec.getId(), + ec.getEdaEcId(), + ec.getType(), + ec.getName(), + ec.getGridOperatorId(), + ec.getSubstationId(), + ec.getTransformerId(), + memberCount + ); } } \ No newline at end of file diff --git a/eeg_frontend/openapi.yaml b/eeg_frontend/openapi.yaml index 1e9ede1..f36633b 100644 --- a/eeg_frontend/openapi.yaml +++ b/eeg_frontend/openapi.yaml @@ -57,9 +57,9 @@ paths: "200": description: OK content: - '*/*': + application/json: schema: - $ref: "#/components/schemas/EnergyCommunity" + $ref: "#/components/schemas/EnergyCommunityDto" put: tags: - energy-community-admin-controller @@ -75,15 +75,15 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/EnergyCommunity" + $ref: "#/components/schemas/EnergyCommunityDto" required: true responses: "200": description: OK content: - '*/*': + application/json: schema: - $ref: "#/components/schemas/EnergyCommunity" + $ref: "#/components/schemas/EnergyCommunityDto" delete: tags: - energy-community-admin-controller @@ -202,7 +202,7 @@ paths: schema: type: array items: - $ref: "#/components/schemas/EnergyCommunity" + $ref: "#/components/schemas/EnergyCommunityDto" post: tags: - energy-community-admin-controller @@ -211,15 +211,15 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/EnergyCommunity" + $ref: "#/components/schemas/EnergyCommunityDto" required: true responses: "200": description: OK content: - '*/*': + application/json: schema: - $ref: "#/components/schemas/EnergyCommunity" + $ref: "#/components/schemas/EnergyCommunityDto" /api/community/metering-points/{id}/eligible-communities: get: tags: @@ -259,7 +259,7 @@ paths: $ref: "#/components/schemas/UserProfileResponse" components: schemas: - EnergyCommunity: + EnergyCommunityDto: type: object properties: id: @@ -281,6 +281,9 @@ components: type: string transformerId: type: string + memberCount: + type: integer + format: int32 MembershipRequest: type: object properties: diff --git a/eeg_frontend/src/app/pages/admin-energy-community/admin-energy-community.ts b/eeg_frontend/src/app/pages/admin-energy-community/admin-energy-community.ts index 9a95254..5a88655 100644 --- a/eeg_frontend/src/app/pages/admin-energy-community/admin-energy-community.ts +++ b/eeg_frontend/src/app/pages/admin-energy-community/admin-energy-community.ts @@ -1,7 +1,7 @@ import {Component, inject, OnInit} from '@angular/core'; import {CommonModule} from '@angular/common'; import {FormBuilder, ReactiveFormsModule, Validators} from '@angular/forms'; -import {EnergyCommunity, EnergyCommunityAdminControllerService} from '../../api'; +import {EnergyCommunityAdminControllerService, EnergyCommunityDto} from '../../api'; @Component({ selector: 'app-admin-energy-community', @@ -13,7 +13,7 @@ export class AdminEnergyCommunity implements OnInit { private ecService = inject(EnergyCommunityAdminControllerService); private fb = inject(FormBuilder); - communities: EnergyCommunity[] = []; + communities: EnergyCommunityDto[] = []; isModalOpen = false; isEditMode = false; @@ -54,7 +54,7 @@ export class AdminEnergyCommunity implements OnInit { this.isModalOpen = true; } - openEditModal(ec: EnergyCommunity): void { + openEditModal(ec: EnergyCommunityDto): void { this.isEditMode = true; this.editingId = ec.id ?? null; this.createForm.patchValue(ec as any); @@ -96,7 +96,7 @@ export class AdminEnergyCommunity implements OnInit { } } - canDelete(ec: EnergyCommunity): boolean { + canDelete(ec: EnergyCommunityDto): boolean { // Wenn dein Backend z.B. die Anzahl der Mitglieder mitliefert: // return !(ec as any).memberCount || (ec as any).memberCount === 0; return true; // Vorerst true, bis die Relation im DTO steht