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;
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<List<EnergyCommunity>> getAllEnergyCommunities() {
return ResponseEntity.ok(service.findAll());
public ResponseEntity<List<EnergyCommunityDto>> getAllEnergyCommunities() {
return ResponseEntity.ok(service.getAllCommunitiesForAdmin());
}
// GET /api/admin/energy-communities/{id}
@GetMapping("/{id}")
public ResponseEntity<EnergyCommunity> getEnergyCommunityById(@PathVariable UUID id) {
return ResponseEntity.ok(service.findById(id));
@GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<EnergyCommunityDto> getEnergyCommunityById(@PathVariable UUID id) {
return ResponseEntity.ok(service.getCommunityById(id));
}
// POST /api/admin/energy-communities
@PostMapping
public ResponseEntity<EnergyCommunity> createEnergyCommunity(@RequestBody EnergyCommunity community) {
EnergyCommunity createdCommunity = service.create(community);
return ResponseEntity.status(HttpStatus.CREATED).body(createdCommunity);
@PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<EnergyCommunityDto> createEnergyCommunity(@RequestBody EnergyCommunityDto community) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.create(community));
}
// PUT /api/admin/energy-communities/{id}
@PutMapping("/{id}")
public ResponseEntity<EnergyCommunity> updateEnergyCommunity(
@PutMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<EnergyCommunityDto> updateEnergyCommunity(
@PathVariable UUID id,
@RequestBody EnergyCommunity community) {
@RequestBody EnergyCommunityDto 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)
private EnergyCommunity energyCommunity;
// 1 = Lokal, 2 = Regional, 3 = BEG
@Column(nullable = false)
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.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<Membership, UUID> {
Integer priorityLevel,
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;
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<EnergyCommunity> findAll() {
return repository.findAll();
public List<EnergyCommunityDto> getAllCommunitiesForAdmin() {
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) {
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<UUID, Integer> 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<UUID, Integer> 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<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":
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:

View File

@ -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