Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@ public ResponseEntity<Void> update(
@PathVariable("sensorId") String sensorId,
@RequestBody SensorUpdateDto dto) {
service.updateSensor(sensorId, dto);
return ResponseEntity.noContent().build();
return ResponseEntity.noContent().build(); // 204 응답 반환
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.factoreal.backend.controller;

import com.factoreal.backend.dto.CreateWorkerRequest;
import com.factoreal.backend.dto.WorkerDto;
import com.factoreal.backend.dto.ZoneManagerResponseDto;
import com.factoreal.backend.service.WorkerService;
Expand All @@ -17,10 +18,18 @@
@RequestMapping("/api/workers")
@RequiredArgsConstructor
@Slf4j
@Tag(name = "작업자 API", description = "작업자 조회 API")
@Tag(name = "작업자 API", description = "작업자 조회 및 생성 API")
public class WorkerController {
private final WorkerService workerService;

@Operation(summary = "작업자 생성", description = "새로운 작업자를 생성하고 접근 가능한 공간들을 선택합니다.")
@PostMapping
public ResponseEntity<Void> createWorker(@RequestBody CreateWorkerRequest request) {
log.info("작업자 생성 요청: {}", request);
workerService.createWorker(request);
return ResponseEntity.ok().build(); // 작업자 생성 성공 시 200 응답
}

@Operation(summary = "전체 작업자 목록 조회", description = "전체 작업자 목록을 조회합니다.")
@GetMapping
public ResponseEntity<List<WorkerDto>> getAllWorkers() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.factoreal.backend.controller;

import com.factoreal.backend.dto.WorkerManagerResponse;
import com.factoreal.backend.service.WorkerManagerService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Tag(name = "공간 담당자 API", description = "공간별 담당자 관리 API")
@Slf4j
@RestController
@RequestMapping("/api/zone-managers")
@RequiredArgsConstructor
public class WorkerManagerController {

private final WorkerManagerService workerManagerService;

@Operation(summary = "공간 담당자 후보 목록 조회", description = "특정 공간의 담당자로 지정 가능한 작업자 목록을 조회합니다.")
@GetMapping("/candidates/{zoneId}")
public ResponseEntity<List<WorkerManagerResponse>> getManagerCandidates(
@Parameter(description = "공간 ID", required = true)
@PathVariable String zoneId) {
log.info("공간 ID: {}의 담당자 후보 목록 조회 요청", zoneId);
return ResponseEntity.ok(workerManagerService.getManagerCandidates(zoneId));
}

@Operation(summary = "공간 담당자 지정", description = "특정 공간의 담당자를 지정합니다.")
@PostMapping("/{zoneId}/assign/{workerId}")
public ResponseEntity<Void> assignManager(
@Parameter(description = "공간 ID", required = true)
@PathVariable String zoneId,
@Parameter(description = "작업자 ID", required = true)
@PathVariable String workerId) {
log.info("공간 ID: {}의 담당자를 작업자 ID: {}로 지정 요청", zoneId, workerId);
workerManagerService.assignManager(zoneId, workerId);
return ResponseEntity.ok().build();
}

@Operation(summary = "현재 공간 담당자 조회", description = "특정 공간의 현재 담당자 정보를 조회합니다.")
@GetMapping("/{zoneId}")
public ResponseEntity<WorkerManagerResponse> getCurrentManager(
@Parameter(description = "공간 ID", required = true)
@PathVariable String zoneId) {
log.info("공간 ID: {}의 현재 담당자 조회 요청", zoneId);
WorkerManagerResponse manager = workerManagerService.getCurrentManager(zoneId);
return manager != null ? ResponseEntity.ok(manager) : ResponseEntity.noContent().build();
// 담당자가 있으면 담당자 정보 반환, 없으면 빈 응답
}
}
21 changes: 21 additions & 0 deletions src/main/java/com/factoreal/backend/dto/CreateWorkerRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.factoreal.backend.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.List;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
// 작업자 생성 요청 DTO (FE -> BE)
public class CreateWorkerRequest {
private String workerId; // 작업자 ID (사원 번호)
private String name; // 작업자 이름
private String phoneNumber; // 연락처
private String email; // 이메일
private List<String> zoneNames; // 출입 가능한 공간명 리스트
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
@Getter
@Setter
@ToString
// Wearable 장치에서 받아오는 데이터 by 우영. 추후 논의 예정
// Wearable 장치에서 받아오는 데이터 by 우영
public class WorkerLocationRequest {
private String workerId;
private String zoneId;
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/com/factoreal/backend/dto/WorkerManagerResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.factoreal.backend.dto;

import com.factoreal.backend.entity.Worker;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
// 공간 담당자 후보 목록 응답 DTO (BE -> FE)
public class WorkerManagerResponse {
private String workerId; // 직원 아이디
private String name; // 직원 이름
private Boolean isManager; // 공간담당자 여부

public static WorkerManagerResponse fromEntity(Worker worker, Boolean isManager) {
return WorkerManagerResponse.builder()
.workerId(worker.getWorkerId())
.name(worker.getName())
.isManager(isManager)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,10 @@ public interface WorkerZoneRepository extends JpaRepository<WorkerZone, WorkerZo

// 특정 zone_id의 담당자 조회 (manageYn = true)
Optional<WorkerZone> findByZoneZoneIdAndManageYnIsTrue(String zoneId);

// 특정 공간을 제외한 다른 공간의 담당자 목록 조회
List<WorkerZone> findByZoneZoneIdNotAndManageYnIsTrue(String zoneId);

// 특정 작업자가 담당자로 있는 공간 조회
Optional<WorkerZone> findByWorkerWorkerIdAndManageYnIsTrue(String workerId);
}
106 changes: 106 additions & 0 deletions src/main/java/com/factoreal/backend/service/WorkerManagerService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package com.factoreal.backend.service;

import com.factoreal.backend.dto.WorkerManagerResponse;
import com.factoreal.backend.entity.Worker;
import com.factoreal.backend.entity.WorkerZone;
import com.factoreal.backend.entity.WorkerZoneId;
import com.factoreal.backend.entity.Zone;
import com.factoreal.backend.repository.WorkerRepository;
import com.factoreal.backend.repository.WorkerZoneRepository;
import com.factoreal.backend.repository.ZoneRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
// 공간 담당자 지정 서비스
public class WorkerManagerService {

private final WorkerRepository workerRepository;
private final WorkerZoneRepository workerZoneRepository;
private final ZoneRepository zoneRepository;

/**
* 특정 공간의 담당자 후보 목록 조회
* - 이미 담당자가 있는 경우: 현재 담당자를 제외한 해당 공간 접근 권한이 있는 작업자 목록
* - 담당자가 없는 경우: 해당 공간 접근 권한이 있는 작업자 목록
* - 다른 공간의 담당자인 작업자는 후보 목록에서 제외
*/
@Transactional(readOnly = true)
public List<WorkerManagerResponse> getManagerCandidates(String zoneId) {
log.info("공간 ID: {}의 담당자 후보 목록 조회", zoneId);

// 1. 현재 해당 공간의 담당자 조회
Optional<WorkerZone> currentManager = workerZoneRepository.findByZoneZoneIdAndManageYnIsTrue(zoneId);

// 2. 현재 공간을 제외한 다른 공간의 담당자 목록 조회 (workerId를 Set으로 묶어서 중복 제거)
Set<String> otherManagerIds = workerZoneRepository.findByZoneZoneIdNotAndManageYnIsTrue(zoneId).stream()
.map(wz -> wz.getWorker().getWorkerId())
.collect(Collectors.toSet());

// 3. 해당 공간에 접근 권한이 있는 작업자 목록 조회
List<WorkerZone> zoneWorkers = workerZoneRepository.findByZoneZoneId(zoneId);

// 4. 현재 담당자와 다른 공간의 담당자를 제외한 후보 목록 생성
List<Worker> candidates = currentManager
.map(manager -> zoneWorkers.stream()
.filter(wz -> !wz.getWorker().getWorkerId().equals(manager.getWorker().getWorkerId())) // 현재 담당자를 제외
.filter(wz -> !otherManagerIds.contains(wz.getWorker().getWorkerId())) // 다른 공간의 담당자인 작업자는 후보 목록에서 제외
.map(WorkerZone::getWorker) // 후보 목록에 포함된 작업자 객체 생성
.collect(Collectors.toList()))
.orElse(zoneWorkers.stream() // 담당자가 없는 경우, 해당 공간 접근 권한이 있는 작업자 목록
.filter(wz -> !otherManagerIds.contains(wz.getWorker().getWorkerId())) // 다른 공간의 담당자인 작업자는 후보 목록에서 제외
.map(WorkerZone::getWorker) // 후보 목록에 포함된 작업자 객체 생성
.collect(Collectors.toList()));

// 4. DTO 변환 (후보 목록의 작업자들은 현재 이 공간의 담당자가 아니므로 isManager = false)
return candidates.stream()
.map(worker -> WorkerManagerResponse.fromEntity(worker, false))
.collect(Collectors.toList());
}

/**
* 특정 공간의 담당자 지정
*/
@Transactional
public void assignManager(String zoneId, String workerId) {
log.info("공간 ID: {}의 담당자를 작업자 ID: {}로 지정", zoneId, workerId);

// 1. 작업자-공간 관계 확인
WorkerZoneId workerZoneId = new WorkerZoneId(workerId, zoneId);
WorkerZone workerZone = workerZoneRepository.findById(workerZoneId)
.orElseThrow(() -> new IllegalArgumentException(
String.format("작업자 ID: %s는 공간 ID: %s에 대한 접근 권한이 없습니다.", workerId, zoneId)));

// 2. 기존 담당자가 있다면 담당자 해제
Optional<WorkerZone> currentManager = workerZoneRepository.findByZoneZoneIdAndManageYnIsTrue(zoneId);
currentManager.ifPresent(manager -> {
manager.setManageYn(false);
workerZoneRepository.save(manager);
});

// 3. 새로운 담당자 지정
workerZone.setManageYn(true);
workerZoneRepository.save(workerZone);
}

/**
* 특정 공간의 현재 담당자 조회
*/
@Transactional(readOnly = true)
public WorkerManagerResponse getCurrentManager(String zoneId) {
log.info("공간 ID: {}의 현재 담당자 조회", zoneId);

return workerZoneRepository.findByZoneZoneIdAndManageYnIsTrue(zoneId)
.map(workerZone -> WorkerManagerResponse.fromEntity(workerZone.getWorker(), true))
.orElse(null);
}
}
61 changes: 45 additions & 16 deletions src/main/java/com/factoreal/backend/service/WorkerService.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package com.factoreal.backend.service;

import com.factoreal.backend.dto.CreateWorkerRequest;
import com.factoreal.backend.dto.WorkerDto;
import com.factoreal.backend.dto.ZoneManagerResponseDto;
import com.factoreal.backend.entity.Worker;
import com.factoreal.backend.entity.WorkerZone;
import com.factoreal.backend.entity.WorkerZoneId;
import com.factoreal.backend.entity.Zone;
import com.factoreal.backend.entity.ZoneHist;
import com.factoreal.backend.repository.WorkerRepository;
import com.factoreal.backend.repository.WorkerZoneRepository;
import com.factoreal.backend.repository.ZoneRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
Expand All @@ -23,13 +26,18 @@ public class WorkerService {
private final WorkerRepository workerRepository;
private final WorkerLocationService workerLocationService;
private final WorkerZoneRepository workerZoneRepository;
private final ZoneRepository zoneRepository;

@Transactional(readOnly = true)
public List<WorkerDto> getAllWorkers() {
log.info("전체 작업자 목록 조회");
List<Worker> workers = workerRepository.findAll();
return workers.stream()
.map(worker -> WorkerDto.fromEntity(worker, false))
.map(worker -> {
// 해당 작업자가 어떤 공간의 담당자인지 확인
boolean isManager = workerZoneRepository.findByWorkerWorkerIdAndManageYnIsTrue(worker.getWorkerId()).isPresent();
return WorkerDto.fromEntity(worker, isManager);
})
.collect(Collectors.toList());
}

Expand Down Expand Up @@ -71,19 +79,40 @@ public ZoneManagerResponseDto getZoneManagerWithLocation(String zoneId) {
// 4. DTO 변환 및 반환
return ZoneManagerResponseDto.fromEntity(manager, currentZone);
}
}

// TODO. 수정되어야 할 로직. 현재는 WorkerZone 테이블에서 공간id로 필터링 되는 모든 작업자를 끌고왔는데,
// 사실 현재 그 공간에서 실제로 작업하고 있는, 즉 들어
// public List<WorkerDto> getWorkersByZoneId(String zoneId) {
// log.info("공간 ID: {}의 작업자 목록 조회", zoneId);
// List<WorkerZone> workerZones = workerZoneRepository.findByZoneZoneId(zoneId);

// return workerZones.stream()
// .map(workerZone -> {
// Worker worker = workerZone.getWorker();
// Boolean isManager = workerZone.getManageYn();
// return WorkerDto.fromEntity(worker, isManager);
// })
// .collect(Collectors.toList());
// }
/**
* 작업자 생성 및 출입 가능 공간 설정
*/
@Transactional
public void createWorker(CreateWorkerRequest request) {
log.info("작업자 생성 요청: {}", request);

// 1. 작업자 정보 저장
Worker worker = Worker.builder()
.workerId(request.getWorkerId())
.name(request.getName())
.phoneNumber(request.getPhoneNumber())
.email(request.getEmail())
.build();

workerRepository.save(worker); // 작업자 정보 저장

// 2. 각 공간명으로 Zone 조회 및 WorkerZone 생성
for (String zoneName : request.getZoneNames()) {
Zone zone = zoneRepository.findByZoneName(zoneName)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 공간명입니다: " + zoneName));

// WorkerZone 생성 (기본적으로 관리자 권한은 없음)
WorkerZone workerZone = WorkerZone.builder()
.id(new WorkerZoneId(worker.getWorkerId(), zone.getZoneId())) // 복합키 생성
.worker(worker)
.zone(zone)
.manageYn(false) // 담당자 권한은 없음이 default
.build();

workerZoneRepository.save(workerZone); // WorkerZone 저장
}

log.info("작업자 생성 완료 - workerId: {}", worker.getWorkerId());
}
}
Loading
Loading