본문 바로가기
프로젝트/DC 빌딩 관리 및 에너지 예측 시스템

[7~8 주차] 프로젝트 진행 과정 (건물 관리자 신청과 등록 API 구현, 전력 고지서 API, RDS 데이터 저장 )

by 서영선 2023. 10. 29.

7주차.  건물 관리자 신청과  등록 API 구현,  전력 고지서 API 구현 

 

 

💡 건물 관리자 신청 , 등록 API 구현

: 회원 가입 시, 기본적으로 건물 사용자로 등록된다. 건물 관리자로 권한을 변경하고 싶으면 건물 관리자를 신청해야 한다.

 

 

< 코드 구현 >

 

  • Controller 
import io.swagger.v3.oas.annotations.Operation;
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.Map;


@Slf4j
@RestController
@Tag(name="manager", description = "건물 관리자 신청 및 등록")
@RequestMapping("/api")
@RequiredArgsConstructor
public class ManagerController {

    private final ManagerService managerService;

    @Operation(method = "post", summary = "건물 관리자 신청 API")
    @PostMapping("/manager/apply")
    public ResponseEntity<?> managerApply(@CurrentUser UserPrincipal user) {
        return managerService.apply(user);
    }

    @Operation(method = "post", summary = "건물 관리자 등록 API")
    @PostMapping("/manager/enroll")
    public ResponseEntity<?> managerEnroll(@CurrentUser UserPrincipal user, @RequestParam Map<String, String> manager) {
        Long managerId = Long.valueOf(manager.get("managerId"));

        return managerService.enroll(user, managerId);
    }

    @Operation(method = "get", summary = "건물 관리자 신청자들 목록 API")
    @GetMapping("/manager/applyList")
    public ResponseEntity<?> managerApplyList(@CurrentUser UserPrincipal user) {
        return managerService.applyList(user);
    }

}

 

 

  • Service
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Slf4j
@Service
@RequiredArgsConstructor
public class ManagerService {

    private final MemberRepository memberRepository;
    private final ManagerApplyRepository managerApplyRepository;

    public ResponseEntity<?> apply(UserPrincipal userPrincipal){
        Optional<Member> member = memberRepository.findById(userPrincipal.getId());
        DefaultAssert.isTrue(member.isPresent(), "유저가 올바르지 않습니다.");
        Member user = member.get();
        if(user.getAuthority()!= Authority.USER){
            throw new IllegalArgumentException("유저는 이미 건물 관리자입니다.");
        }

        managerApplyRepository.save(new Manager(user));
        ApiResponse apiResponse = ApiResponse.builder().check(true).information(Message.builder().message("건물 관리자 신청이 완료되었습니다.").build()).build();

        return ResponseEntity.ok(apiResponse);
    }

    public ResponseEntity<?> enroll(UserPrincipal userPrincipal, Long managerId){
        Optional<Member> member = memberRepository.findById(userPrincipal.getId());
        DefaultAssert.isTrue(member.isPresent(), "유저가 올바르지 않습니다.");

        Optional<Manager> applyManager = managerApplyRepository.findById(managerId);
        DefaultAssert.isTrue(applyManager.isPresent(), "관리자를 신청 id가 올바르지 않습니다.");

        memberRepository.updateAuthority(applyManager.get().getMember().getId(), Authority.MANAGER);   // 해당 유저 매니저로 등록
        managerApplyRepository.deleteById(managerId);       // 신청 목록에서 해당 유저 삭제
        ApiResponse apiResponse = ApiResponse.builder().check(true).information(Message.builder().message("건물 관리자 등록이 완료되었습니다.").build()).build();

        return ResponseEntity.ok(apiResponse);
    }


    public ResponseEntity<?> applyList(UserPrincipal userPrincipal){
        Optional<Member> member = memberRepository.findById(userPrincipal.getId());
        DefaultAssert.isTrue(member.isPresent(), "유저가 올바르지 않습니다.");
        
        if(member.get().getAuthority()==Authority.USER){
            throw new IllegalArgumentException("건물 관리자만 리스트를 볼 수 있습니다.");
        }
         
        List<ManagerApplyListDto> list = new ArrayList<>();
        List<Manager> memberList = managerApplyRepository.findAll();
        for (Manager manager : memberList) {
            Long id = manager.getId();
            Member member1 = manager.getMember();
            ManagerApplyListDto managerDto = new ManagerApplyListDto(id, member1.getId(), member1.getUsername(), member1.getEmail());
            list.add(managerDto);
        }

        return ResponseEntity.ok(list);
    }
}

 

 

  • Dto - 건물 신청자 목록 Dto
import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class ManagerApplyListDto {

    private Long managerId;
    private Long memberId;
    private String memberName;
    private String memberEmail;

}

 

 

  • Domain
@Entity
@Data
@EqualsAndHashCode(of = "id")
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Manager {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    @Column(name = "manager_id")
    public Long id;


    @OneToOne
    @JsonIgnore
    @JoinColumn(name = "member_id")
    private Member member;


    public Manager(Member member) {
        this.member = member;
    }
}

 

 

  • Repository
import jakarta.transaction.Transactional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface ManagerApplyRepository extends JpaRepository<Manager, Long> {
    @Override
    List<Manager> findAll();

    @Modifying
    @Transactional
    void deleteById(Long id);
}

 

 

 

< 구현 결과 >

 

1. 건물 관리자 신청 - 유저의 토큰을 Authorization에 넣고, 해당 API에 POST하면 건물 관리자 신청이 완료된다.

 

 

2. 건물 관리자 신청자 목록 보기 - 건물 관리자의 토큰을 넣고 해당 API를 GET하면 신청자 목록을 확인할 수 있다.

 

3. 건물 관리자로 등록하기 - 건물 관리자의 토큰을 넣고 해당 API와 건물 관리자로 등록할 2번의 managerId 함께  POST하면 신청자 목록을 확인할 수 있다.

 

 

 

💡 전력 소비량과 요금 계산 API 구현

: DB에 저장된 전력 소비량을 계산해, 한달 주기의 전력 소비량을 구현한다.

전력 소비량(kW) * 단위 요금 (kWh)를 구해 유저가 사용하는 건물 층의 전력 요금을 계산하는 API를 구현한다.

 

 

 

< 코드 구현 >

 

  • Service
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Slf4j
@Service
@RequiredArgsConstructor
public class CalculateService {

    private final ApiService apiService;
    private final MemberRepository memberRepository;
    private final BuildingEnergyPriceRepository buildingEnergyPriceRepository;
    private final BuildingPerTenMinuteRepository buildingPerTenMinuteRepository;


    public TotalCostDto calculateCost(@CurrentUser UserPrincipal userPrincipal) throws IOException {   // 전력 요금 계산 서비스
        Optional<Member> member = memberRepository.findById(userPrincipal.getId());
        DefaultAssert.isTrue(member.isPresent(), "유저가 올바르지 않습니다.");
        Member user = member.get();

        List<Integer> priceList = new ArrayList<>();
        List<Integer> costList = new ArrayList<>();

        LocalDateTime current = LocalDateTime.now();
        LocalDateTime currentLocal = LocalDateTime.of(current.getYear(), current.getMonthValue(), current.getDayOfMonth(), current.getHour(), ((current.getMinute())/10)*10 );

        // 전달, 전전달 전력 요금 계산
        for(int i=0;i<2; i++){
            UnitCostDto unitCostDto = new UnitCostDto(LocalDate.now().getYear(), LocalDate.now().getMonthValue()-8+i, 31, 74);
            Double unitCost = apiService.getApi(unitCostDto); // 단위 전력 요금
            Double ConsumptionAll = buildingEnergyPriceRepository.findByBuildingAndFloor(LocalDate.now().getMonthValue()-8+i,user.getBuilding(), user.getFloor());

            costList.add((int)(ConsumptionAll/100));    // kW기준 전력 사용량 저장
            priceList.add((int)(ConsumptionAll * unitCost)/100);   // 원 기준 전력 요금 저장 -> 10분 단위와 kWh이므로 100을 나누어 주어야 함 (*10 /1000)
        }


        // 이번 달 실시간 전력 사용량 계산
        Timestamp now = new Timestamp(System.currentTimeMillis());
        Timestamp startDt = new Timestamp(now.getYear(),now.getMonth()-6,1, 0,0,0, 0);
        Timestamp betweenEndDt = new Timestamp(now.getYear(),now.getMonth()-6,now.getDate(), now.getHours(),now.getMinutes(),now.getSeconds(), now.getNanos());

        // 이번 달 전력 단위 요금 계산
        UnitCostDto unitCostDto = new UnitCostDto(LocalDate.now().getYear(), LocalDate.now().getMonthValue()-6, 31, 74);
        Double unitCost = apiService.getApi(unitCostDto);

        // 이번 달 실시간 전력 요금 계산
        Integer consumptionByTimeStamp = buildingPerTenMinuteRepository.findConsumptionByTimestampAndBuildingAndFloor(startDt, betweenEndDt, user.getBuilding() ,user.getFloor());
        priceList.add((int)(consumptionByTimeStamp * unitCost)/100);
        costList.add((int)(consumptionByTimeStamp/100));

        return new TotalCostDto(currentLocal,priceList,costList);

    }


}

 

 

  • Controller
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;

@RestController
@RequiredArgsConstructor
@Tag(name="UserBill", description = "전력 사용량, 전력 비용 2달전, 1달전, 이번달 구하는 API")
@RequestMapping("/api")
public class UnitCostController {


    private final CalculateService calculateService;

    @Operation(method = "get", summary = "2달전, 1달전, 이번달 전력 요금(원), 전력 사용량(kW) API ")
    @GetMapping("/bill")       // 전력 단위 요금
    public TotalCostDto energyPriceByMonth(@CurrentUser UserPrincipal userPrincipal) throws IOException {
        return calculateService.calculateCost(userPrincipal);
    }


}

 

 

< 구현 결과 >

 

: 건물 사용자의 토큰을 넣고 해당 API를 GET하면, 유저가 거주하는 층의 소비전력과 전력 요금 3개월 치(2달전, 1달전, 이번 달)와 계산한 기준 시간이 반환된다.

 

 

 

 

 

 

8주차.  RDS 데이터 저장 

데이터를 통일하고, 원활한 개발을 위해서 RDS에 저장이 필요했다.

초기 데이터가 500만개가 넘어서 저장하는 데 시간을 줄이려 노력했다.

 

 

💡  TRY 1.  IntelliJ에서 csv 파일 읽어 저장하는 코드 구현하여 저장

→ 시간이 너무 오래 걸린다.......

 

 

💡  TRY 2.  MySQL의 Table Data Import Wizard를 이용하여 저장

이것도 시간이 너무 오래 걸린다......😢

 

 

💡  TRY 2.  MySQL에서 LOAD DATA INFILE 이용하여 저장

→ 20배 빠른 속도로 저장할 수 있었지만, 실행하기까지 수많은 오류와 설정을 고치느라 애를 먹었다.

 

 

 

< 사용한 코드 >

LOAD DATA LOCAL INFILE 'C:\\Users\\User\\[1분 단위]아파트_동별_소비전력_전력분배_2022-07-18 00.00.00~2023-08-30 10.39.00.csv' 
INTO TABLE BuildingPerMinute 
CHARACTER SET utf8 
FIELDS TERMINATED BY ',' 
LINES TERMINATED BY '\n';

 

 

 

 

 

 

 

 

캡스톤_중간보고서_7조.pdf
1.01MB

 

 

 

 

7~8 주차 후기
이번 두 주차는 코드 개발은 괜찮았지만, RDS에 데이터를 저장하는 과정이 고단했다,,,
하루 종일 돌려도 데이터를 다 저장하지 못할것 같아, 여러 방법들을 시도해보면서 새로운 방법들을 알게 되었다.
MySQL Client에서 이런 저런 설정을 해주었는데, 아직 CLI 방식은 너무 어려운 것 같다,,,
그래도, 이제 데이터를 RDS에 넣어서 다른 팀원들이 긴 데이터 저장 과정 없이 바로 데이터를 이용할 수 있게 하여 뿌듯하다. ..✨

 

 

 

 

댓글