Dashboard 용 웹서비스 추가.
This commit is contained in:
parent
506440bc2e
commit
fba94438fa
|
|
@ -0,0 +1,71 @@
|
|||
package com.goi.erp.controller;
|
||||
|
||||
import com.goi.erp.common.permission.PermissionChecker;
|
||||
import com.goi.erp.common.permission.PermissionSet;
|
||||
import com.goi.erp.dto.DriverDailyStatHistoryResponseDto;
|
||||
import com.goi.erp.dto.DriverDailyStatResponseDto;
|
||||
import com.goi.erp.service.DriverDailyStatService;
|
||||
import com.goi.erp.token.PermissionAuthenticationToken;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/driver-daily-stat")
|
||||
@RequiredArgsConstructor
|
||||
public class DriverDailyStatController {
|
||||
|
||||
private final DriverDailyStatService driverDailyStatService;
|
||||
|
||||
private PermissionSet getPermission() {
|
||||
PermissionAuthenticationToken auth =
|
||||
(PermissionAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
if (auth == null || auth.getPermissionSet() == null) {
|
||||
throw new AccessDeniedException("Permission information is missing");
|
||||
}
|
||||
|
||||
return auth.getPermissionSet();
|
||||
}
|
||||
|
||||
|
||||
// READ (대시보드: date default=오늘, driverId default=전체)
|
||||
@GetMapping
|
||||
public ResponseEntity<List<DriverDailyStatResponseDto>> getDashboard(
|
||||
@RequestParam(required = false)
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date,
|
||||
@RequestParam(required = false) Long driverId) {
|
||||
|
||||
PermissionSet permissions = getPermission();
|
||||
if (!PermissionChecker.canReadCRMAll(permissions)) {
|
||||
throw new AccessDeniedException("You do not have permission to read dispatch daily status");
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(driverDailyStatService.getDashboard(date, driverId));
|
||||
}
|
||||
|
||||
// READ HISTORY (단일 드라이버 누적 이력: driverId 필수, date default=오늘)
|
||||
@GetMapping("/history")
|
||||
public ResponseEntity<DriverDailyStatHistoryResponseDto> getDriverHistory(
|
||||
@RequestParam Long driverId,
|
||||
@RequestParam(required = false)
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date) {
|
||||
|
||||
PermissionSet permissions = getPermission();
|
||||
if (!PermissionChecker.canReadCRMAll(permissions)) {
|
||||
throw new AccessDeniedException("You do not have permission to read dispatch history");
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(driverDailyStatService.getDriverHistory(date, driverId));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.goi.erp.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DriverDailyStatHistoryPointDto {
|
||||
|
||||
private LocalDateTime t; // 픽업 시각
|
||||
private String customerName; // 고객명 (cus_name)
|
||||
private BigDecimal qty; // 이 픽업의 양
|
||||
private BigDecimal cumQty; // 누적 픽업량
|
||||
private Long cumCompleted; // 누적 방문 완료 수
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.goi.erp.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DriverDailyStatHistoryResponseDto {
|
||||
|
||||
private Long driverId; // 드라이버 ID
|
||||
private LocalDate date; // 조회 일자
|
||||
private List<DriverDailyStatHistoryPointDto> points; // 시각순 누적 포인트 (라인 1개)
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.goi.erp.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DriverDailyStatResponseDto {
|
||||
|
||||
private Long ddsId; // 행 식별자 (프론트 row key용, vd.ved_id)
|
||||
private String ddsStatus; // 배차 상태 (O 등)
|
||||
private Integer ddsShift; // 교대(shift)
|
||||
private LocalDate ddsDate; // 배차 일자
|
||||
private String ddsNumber; // 차량 번호 (예: 9호)
|
||||
private Long ddsDriverId; // 드라이버 ID (필터/드롭다운용)
|
||||
private String ddsMainDriver; // 주 드라이버
|
||||
private String ddsSubDriver; // 보조 드라이버
|
||||
private String ddsKm; // 이동거리 (예: "35.78 km")
|
||||
private String ddsMin; // 운행시간 (예: "198.00 min")
|
||||
private Integer ddsEvent; // 안전 이벤트 수
|
||||
private BigDecimal ddsTotalQty; // 실제 픽업량 합계
|
||||
private BigDecimal ddsPlannedQty; // 예상 픽업량 합계 (목표)
|
||||
private Long ddsCompletedCount; // 방문 완료(F) 수
|
||||
private Long ddsPlannedCount; // 전체 방문 목표 수
|
||||
private LocalDateTime ddsStartAt; // 운행 시작
|
||||
private LocalDateTime ddsPausedAt; // 일시정지
|
||||
private LocalDateTime ddsEndAt; // 운행 종료
|
||||
private String ddsEndReason; // 종료 사유
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.goi.erp.entity;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import org.hibernate.annotations.Immutable;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Immutable // 읽기 전용 뷰
|
||||
@Table(schema = "rpt", name = "vw_driver_daily_stat") // 실제 생성한 스키마/뷰 이름에 맞춰 조정
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class DriverDailyStat {
|
||||
|
||||
@Id
|
||||
private Long ddsId; // vd.ved_id (뷰 PK용, SELECT에 dds_id 추가 필요)
|
||||
private String ddsStatus; // 배차 상태 (O 등)
|
||||
private Integer ddsShift; // 교대(shift)
|
||||
private LocalDate ddsDate; // 배차 일자
|
||||
private String ddsNumber; // 차량 번호 (예: 9호)
|
||||
private Long ddsDriverId; // 드라이버 ID (필터/드롭다운용)
|
||||
private String ddsMainDriver; // 주 드라이버
|
||||
private String ddsSubDriver; // 보조 드라이버
|
||||
private String ddsKm; // 이동거리 (뷰에서 텍스트, 예: "35.78 km")
|
||||
private String ddsMin; // 운행시간 (뷰에서 텍스트, 예: "198.00 min")
|
||||
private Integer ddsEvent; // 안전 이벤트 수
|
||||
private BigDecimal ddsTotalQty; // 실제 픽업량 합계
|
||||
private BigDecimal ddsPlannedQty; // 예상 픽업량 합계 (목표)
|
||||
private Long ddsCompletedCount; // 방문 완료(F) 수
|
||||
private Long ddsPlannedCount; // 전체 방문 목표 수
|
||||
private LocalDateTime ddsStartAt; // 운행 시작
|
||||
private LocalDateTime ddsPausedAt; // 일시정지
|
||||
private LocalDateTime ddsEndAt; // 운행 종료
|
||||
private String ddsEndReason; // 종료 사유
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.goi.erp.repository;
|
||||
|
||||
import com.goi.erp.entity.DriverDailyStat;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface DriverDailyStatRepository extends JpaRepository<DriverDailyStat, Long> {
|
||||
|
||||
// 날짜 기준 전체 드라이버 (운행 시작 시간 순)
|
||||
List<DriverDailyStat> findByDdsDateOrderByDdsStartAtAsc(LocalDate ddsDate);
|
||||
|
||||
// 날짜 + 특정 드라이버 (운행 시작 시간 순)
|
||||
List<DriverDailyStat> findByDdsDateAndDdsDriverIdOrderByDdsStartAtAsc(LocalDate ddsDate, Long ddsDriverId);
|
||||
|
||||
// [단일 드라이버] 픽업 이벤트 기반 누적 이력 (시각순) — 라인 차트용
|
||||
// - crm.customer_daily_order 의 cdo_pickup_at 으로 누적합
|
||||
// - return: [0]=픽업 시각, [1]=누적 픽업량, [2]=누적 방문완료 수
|
||||
@Query(value = """
|
||||
SELECT cdo.cdo_pickup_at AS t,
|
||||
c.cus_name AS cus_name,
|
||||
cdo.cdo_quantity AS qty,
|
||||
SUM(cdo.cdo_quantity) OVER (ORDER BY cdo.cdo_pickup_at) AS cum_qty,
|
||||
COUNT(*) FILTER (WHERE cast(cdo.cdo_status as text) = 'F')
|
||||
OVER (ORDER BY cdo.cdo_pickup_at) AS cum_completed
|
||||
FROM crm.customer_daily_order cdo
|
||||
LEFT JOIN crm.customer c ON c.cus_id = cdo.cdo_customer_id
|
||||
WHERE cdo.cdo_order_date = :date
|
||||
AND cdo.cdo_driver_id = :driverId
|
||||
AND cdo.cdo_pickup_at IS NOT NULL
|
||||
ORDER BY cdo.cdo_pickup_at
|
||||
""", nativeQuery = true)
|
||||
List<Object[]> findDriverPickupHistory(@Param("date") LocalDate date,
|
||||
@Param("driverId") Long driverId);
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
package com.goi.erp.service;
|
||||
|
||||
import com.goi.erp.dto.DriverDailyStatHistoryPointDto;
|
||||
import com.goi.erp.dto.DriverDailyStatHistoryResponseDto;
|
||||
import com.goi.erp.dto.DriverDailyStatResponseDto;
|
||||
import com.goi.erp.entity.DriverDailyStat;
|
||||
import com.goi.erp.repository.DriverDailyStatRepository;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DriverDailyStatService {
|
||||
|
||||
private final DriverDailyStatRepository driverDailyStatRepository;
|
||||
|
||||
/**
|
||||
* 대시보드 조회
|
||||
* @param date 조회 일자 (null 이면 오늘)
|
||||
* @param driverId 드라이버 ID (null 이면 전체)
|
||||
*/
|
||||
public List<DriverDailyStatResponseDto> getDashboard(LocalDate date, Long driverId) {
|
||||
|
||||
LocalDate targetDate = (date == null) ? LocalDate.now() : date;
|
||||
|
||||
List<DriverDailyStat> rows = (driverId == null)
|
||||
? driverDailyStatRepository.findByDdsDateOrderByDdsStartAtAsc(targetDate)
|
||||
: driverDailyStatRepository.findByDdsDateAndDdsDriverIdOrderByDdsStartAtAsc(targetDate, driverId);
|
||||
|
||||
return rows.stream().map(this::mapToDto).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 단일 드라이버 누적 이력 (이벤트 시각 기반, 라인 차트용)
|
||||
* @param date 조회 일자 (null 이면 오늘)
|
||||
* @param driverId 드라이버 ID (필수)
|
||||
*/
|
||||
public DriverDailyStatHistoryResponseDto getDriverHistory(LocalDate date, Long driverId) {
|
||||
|
||||
LocalDate targetDate = (date == null) ? LocalDate.now() : date;
|
||||
|
||||
List<DriverDailyStatHistoryPointDto> points = driverDailyStatRepository
|
||||
.findDriverPickupHistory(targetDate, driverId)
|
||||
.stream()
|
||||
.map(r -> DriverDailyStatHistoryPointDto.builder()
|
||||
.t(((Timestamp) r[0]).toLocalDateTime())
|
||||
.customerName((String) r[1])
|
||||
.qty(r[2] == null ? BigDecimal.ZERO : (BigDecimal) r[2])
|
||||
.cumQty(r[3] == null ? BigDecimal.ZERO : (BigDecimal) r[3])
|
||||
.cumCompleted(r[4] == null ? 0L : ((Number) r[4]).longValue())
|
||||
.build())
|
||||
.toList();
|
||||
|
||||
return DriverDailyStatHistoryResponseDto.builder()
|
||||
.driverId(driverId)
|
||||
.date(targetDate)
|
||||
.points(points)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* ENTITY → RESPONSE DTO
|
||||
*/
|
||||
public DriverDailyStatResponseDto mapToDto(DriverDailyStat s) {
|
||||
|
||||
if (s == null) return null;
|
||||
|
||||
return DriverDailyStatResponseDto.builder()
|
||||
.ddsId(s.getDdsId())
|
||||
.ddsDriverId(s.getDdsDriverId())
|
||||
.ddsStatus(s.getDdsStatus())
|
||||
.ddsShift(s.getDdsShift())
|
||||
.ddsDate(s.getDdsDate())
|
||||
.ddsNumber(s.getDdsNumber())
|
||||
.ddsMainDriver(s.getDdsMainDriver())
|
||||
.ddsSubDriver(s.getDdsSubDriver())
|
||||
.ddsKm(s.getDdsKm())
|
||||
.ddsMin(s.getDdsMin())
|
||||
.ddsEvent(s.getDdsEvent())
|
||||
.ddsTotalQty(s.getDdsTotalQty())
|
||||
.ddsPlannedQty(s.getDdsPlannedQty())
|
||||
.ddsCompletedCount(s.getDdsCompletedCount())
|
||||
.ddsPlannedCount(s.getDdsPlannedCount())
|
||||
.ddsStartAt(s.getDdsStartAt())
|
||||
.ddsPausedAt(s.getDdsPausedAt())
|
||||
.ddsEndAt(s.getDdsEndAt())
|
||||
.ddsEndReason(s.getDdsEndReason())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue