fixed Dto mapping in EnergyCommunityAdminController and openapi definition

This commit is contained in:
Bernhard Müller 2026-06-24 08:33:15 +02:00
parent 81ee804ae1
commit 8b57f97d88
8 changed files with 130 additions and 49 deletions

View File

@ -1,6 +1,6 @@
package at.mueller.eeg.backend.community.api; 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 at.mueller.eeg.backend.community.service.EnergyCommunityService;
import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Content;
@ -30,32 +30,31 @@ public class EnergyCommunityAdminController {
content = @Content( content = @Content(
mediaType = MediaType.APPLICATION_JSON_VALUE, mediaType = MediaType.APPLICATION_JSON_VALUE,
array = @ArraySchema( array = @ArraySchema(
schema = @Schema(implementation = EnergyCommunity.class) schema = @Schema(implementation = EnergyCommunityDto.class)
) )
) )
) )
public ResponseEntity<List<EnergyCommunity>> getAllEnergyCommunities() { public ResponseEntity<List<EnergyCommunityDto>> getAllEnergyCommunities() {
return ResponseEntity.ok(service.findAll()); return ResponseEntity.ok(service.getAllCommunitiesForAdmin());
} }
// GET /api/admin/energy-communities/{id} // GET /api/admin/energy-communities/{id}
@GetMapping("/{id}") @GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<EnergyCommunity> getEnergyCommunityById(@PathVariable UUID id) { public ResponseEntity<EnergyCommunityDto> getEnergyCommunityById(@PathVariable UUID id) {
return ResponseEntity.ok(service.findById(id)); return ResponseEntity.ok(service.getCommunityById(id));
} }
// POST /api/admin/energy-communities // POST /api/admin/energy-communities
@PostMapping @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<EnergyCommunity> createEnergyCommunity(@RequestBody EnergyCommunity community) { public ResponseEntity<EnergyCommunityDto> createEnergyCommunity(@RequestBody EnergyCommunityDto community) {
EnergyCommunity createdCommunity = service.create(community); return ResponseEntity.status(HttpStatus.CREATED).body(service.create(community));
return ResponseEntity.status(HttpStatus.CREATED).body(createdCommunity);
} }
// PUT /api/admin/energy-communities/{id} // PUT /api/admin/energy-communities/{id}
@PutMapping("/{id}") @PutMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<EnergyCommunity> updateEnergyCommunity( public ResponseEntity<EnergyCommunityDto> updateEnergyCommunity(
@PathVariable UUID id, @PathVariable UUID id,
@RequestBody EnergyCommunity community) { @RequestBody EnergyCommunityDto community) {
return ResponseEntity.ok(service.update(id, community)); return ResponseEntity.ok(service.update(id, community));
} }

View File

@ -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
) {}

View File

@ -24,7 +24,6 @@ public class Membership {
@JoinColumn(name = "energy_community_id", nullable = false) @JoinColumn(name = "energy_community_id", nullable = false)
private EnergyCommunity energyCommunity; private EnergyCommunity energyCommunity;
// 1 = Lokal, 2 = Regional, 3 = BEG
@Column(nullable = false) @Column(nullable = false)
private Integer priorityLevel; private Integer priorityLevel;

View File

@ -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);
}

View File

@ -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.Membership;
import at.mueller.eeg.backend.community.domain.MembershipStatus; import at.mueller.eeg.backend.community.domain.MembershipStatus;
import org.springframework.data.jpa.repository.JpaRepository; 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.List;
import java.util.UUID; import java.util.UUID;
@ -18,4 +20,9 @@ public interface MembershipRepository extends JpaRepository<Membership, UUID> {
Integer priorityLevel, Integer priorityLevel,
List<MembershipStatus> statuses List<MembershipStatus> 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<Object[]> countActiveMembersForCommunities(@Param("communityIds") List<UUID> communityIds);
} }

View File

@ -1,53 +1,95 @@
package at.mueller.eeg.backend.community.service; 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.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.EnergyCommunityRepository;
import at.mueller.eeg.backend.community.repository.MembershipRepository;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class EnergyCommunityService { public class EnergyCommunityService {
private final EnergyCommunityRepository repository; private final EnergyCommunityRepository energyCommunityRepository;
private final MembershipRepository membershipRepository;
private final EnergyCommunityMapper energyCommunityMapper;
public List<EnergyCommunity> findAll() { public List<EnergyCommunityDto> getAllCommunitiesForAdmin() {
return repository.findAll(); var communities = energyCommunityRepository.findAll();
List<UUID> communityIds = communities.stream().map(EnergyCommunity::getId).toList();
Map<UUID, Integer> memberCountMap = getMemberCountsForIds(communityIds);
return communities.stream()
.map(ec -> mapToDto(ec, memberCountMap.getOrDefault(ec.getId(), 0)))
.toList();
} }
public EnergyCommunity findById(UUID id) { public EnergyCommunityDto getCommunityById(UUID communityId) {
return repository.findById(id) EnergyCommunity ec = energyCommunityRepository.findById(communityId)
.orElseThrow(() -> new IllegalArgumentException("Energy Community mit ID " + id + " nicht gefunden.")); .orElseThrow(() -> new IllegalArgumentException("Energy Community mit ID " + communityId + " nicht gefunden."));
Map<UUID, Integer> memberCountMap = getMemberCountsForIds(List.of(communityId));
return mapToDto(ec, memberCountMap.getOrDefault(communityId, 0));
} }
@Transactional @Transactional
public EnergyCommunity create(EnergyCommunity community) { public EnergyCommunityDto create(EnergyCommunityDto community) {
return repository.save(community); return energyCommunityMapper.toDto(
energyCommunityRepository.save(
energyCommunityMapper.toEntity(community, new EnergyCommunity())
), 0);
} }
@Transactional @Transactional
public EnergyCommunity update(UUID id, EnergyCommunity updatedData) { public EnergyCommunityDto update(UUID id, EnergyCommunityDto dto) {
EnergyCommunity existing = findById(id); EnergyCommunity existingEntity = energyCommunityRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Energiegemeinschaft nicht gefunden"));
energyCommunityMapper.toEntity(dto, existingEntity);
EnergyCommunity updatedEntity = energyCommunityRepository.save(existingEntity);
Map<UUID, Integer> memberCountMap = getMemberCountsForIds(List.of(id));
existing.setEdaEcId(updatedData.getEdaEcId()); return energyCommunityMapper.toDto(updatedEntity, memberCountMap.getOrDefault(id, 0));
existing.setType(updatedData.getType());
existing.setName(updatedData.getName());
existing.setGridOperatorId(updatedData.getGridOperatorId());
existing.setSubstationId(updatedData.getSubstationId());
existing.setTransformerId(updatedData.getTransformerId());
return repository.save(existing);
} }
@Transactional @Transactional
public void delete(UUID id) { public void delete(UUID id) {
if (!repository.existsById(id)) { if (!energyCommunityRepository.existsById(id)) {
throw new IllegalArgumentException("Energy Community mit ID " + id + " existiert nicht."); throw new IllegalArgumentException("Energy Community mit ID " + id + " existiert nicht.");
} }
repository.deleteById(id); energyCommunityRepository.deleteById(id);
}
private Map<UUID, Integer> getMemberCountsForIds(List<UUID> 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
);
} }
} }

View File

@ -57,9 +57,9 @@ paths:
"200": "200":
description: OK description: OK
content: content:
'*/*': application/json:
schema: schema:
$ref: "#/components/schemas/EnergyCommunity" $ref: "#/components/schemas/EnergyCommunityDto"
put: put:
tags: tags:
- energy-community-admin-controller - energy-community-admin-controller
@ -75,15 +75,15 @@ paths:
content: content:
application/json: application/json:
schema: schema:
$ref: "#/components/schemas/EnergyCommunity" $ref: "#/components/schemas/EnergyCommunityDto"
required: true required: true
responses: responses:
"200": "200":
description: OK description: OK
content: content:
'*/*': application/json:
schema: schema:
$ref: "#/components/schemas/EnergyCommunity" $ref: "#/components/schemas/EnergyCommunityDto"
delete: delete:
tags: tags:
- energy-community-admin-controller - energy-community-admin-controller
@ -202,7 +202,7 @@ paths:
schema: schema:
type: array type: array
items: items:
$ref: "#/components/schemas/EnergyCommunity" $ref: "#/components/schemas/EnergyCommunityDto"
post: post:
tags: tags:
- energy-community-admin-controller - energy-community-admin-controller
@ -211,15 +211,15 @@ paths:
content: content:
application/json: application/json:
schema: schema:
$ref: "#/components/schemas/EnergyCommunity" $ref: "#/components/schemas/EnergyCommunityDto"
required: true required: true
responses: responses:
"200": "200":
description: OK description: OK
content: content:
'*/*': application/json:
schema: schema:
$ref: "#/components/schemas/EnergyCommunity" $ref: "#/components/schemas/EnergyCommunityDto"
/api/community/metering-points/{id}/eligible-communities: /api/community/metering-points/{id}/eligible-communities:
get: get:
tags: tags:
@ -259,7 +259,7 @@ paths:
$ref: "#/components/schemas/UserProfileResponse" $ref: "#/components/schemas/UserProfileResponse"
components: components:
schemas: schemas:
EnergyCommunity: EnergyCommunityDto:
type: object type: object
properties: properties:
id: id:
@ -281,6 +281,9 @@ components:
type: string type: string
transformerId: transformerId:
type: string type: string
memberCount:
type: integer
format: int32
MembershipRequest: MembershipRequest:
type: object type: object
properties: properties:

View File

@ -1,7 +1,7 @@
import {Component, inject, OnInit} from '@angular/core'; import {Component, inject, OnInit} from '@angular/core';
import {CommonModule} from '@angular/common'; import {CommonModule} from '@angular/common';
import {FormBuilder, ReactiveFormsModule, Validators} from '@angular/forms'; import {FormBuilder, ReactiveFormsModule, Validators} from '@angular/forms';
import {EnergyCommunity, EnergyCommunityAdminControllerService} from '../../api'; import {EnergyCommunityAdminControllerService, EnergyCommunityDto} from '../../api';
@Component({ @Component({
selector: 'app-admin-energy-community', selector: 'app-admin-energy-community',
@ -13,7 +13,7 @@ export class AdminEnergyCommunity implements OnInit {
private ecService = inject(EnergyCommunityAdminControllerService); private ecService = inject(EnergyCommunityAdminControllerService);
private fb = inject(FormBuilder); private fb = inject(FormBuilder);
communities: EnergyCommunity[] = []; communities: EnergyCommunityDto[] = [];
isModalOpen = false; isModalOpen = false;
isEditMode = false; isEditMode = false;
@ -54,7 +54,7 @@ export class AdminEnergyCommunity implements OnInit {
this.isModalOpen = true; this.isModalOpen = true;
} }
openEditModal(ec: EnergyCommunity): void { openEditModal(ec: EnergyCommunityDto): void {
this.isEditMode = true; this.isEditMode = true;
this.editingId = ec.id ?? null; this.editingId = ec.id ?? null;
this.createForm.patchValue(ec as any); 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: // Wenn dein Backend z.B. die Anzahl der Mitglieder mitliefert:
// return !(ec as any).memberCount || (ec as any).memberCount === 0; // return !(ec as any).memberCount || (ec as any).memberCount === 0;
return true; // Vorerst true, bis die Relation im DTO steht return true; // Vorerst true, bis die Relation im DTO steht