목표량 취합을 위한 웹서비스 추가.
This commit is contained in:
parent
fba94438fa
commit
e7faffa262
|
|
@ -0,0 +1,51 @@
|
|||
package com.goi.erp.controller;
|
||||
|
||||
import com.goi.erp.dto.CustomerDailyUcoRequestDto;
|
||||
import com.goi.erp.dto.CustomerDailyUcoResponseDto;
|
||||
import com.goi.erp.service.CustomerDailyUcoService;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/customer-daily-uco")
|
||||
@RequiredArgsConstructor
|
||||
public class CustomerDailyUcoController {
|
||||
|
||||
private final CustomerDailyUcoService customerDailyUcoService;
|
||||
|
||||
/** 대시보드: 특정 일자 전체 드라이버 목표/실적 */
|
||||
@GetMapping
|
||||
public ResponseEntity<List<CustomerDailyUcoResponseDto>> getByDate(
|
||||
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date) {
|
||||
return ResponseEntity.ok(customerDailyUcoService.getByDate(date));
|
||||
}
|
||||
|
||||
/** 단일 드라이버 + 일자 */
|
||||
@GetMapping("/driver")
|
||||
public ResponseEntity<CustomerDailyUcoResponseDto> getByDriverAndDate(
|
||||
@RequestParam Long driverId,
|
||||
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date) {
|
||||
CustomerDailyUcoResponseDto dto = customerDailyUcoService.getByDriverAndDate(driverId, date);
|
||||
return dto != null ? ResponseEntity.ok(dto) : ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
/** MIS 08:00 목표 동기화 (upsert) — 스케줄러 pull 또는 MIS push 둘 다 사용 */
|
||||
@PostMapping("/target")
|
||||
public ResponseEntity<Void> syncTarget(@RequestBody CustomerDailyUcoRequestDto dto) {
|
||||
customerDailyUcoService.syncTarget(dto);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
/** 드라이버 언로딩 입력 (실측 + 사진) */
|
||||
@PutMapping("/unload")
|
||||
public ResponseEntity<CustomerDailyUcoResponseDto> recordUnload(@RequestBody CustomerDailyUcoRequestDto dto) {
|
||||
return ResponseEntity.ok(customerDailyUcoService.recordUnload(dto));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.goi.erp.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class CustomerDailyUcoRequestDto {
|
||||
|
||||
private LocalDate cduDate; // 대상 날짜 (YYYY-MM-DD)
|
||||
|
||||
// 드라이버 식별 (MIS 는 외부 driverId, ERP 는 uuid 로 호출)
|
||||
private String cduExternalDriverId; // MIS driverId → employee_external_map 참조
|
||||
private Long cduDriverId; // 배정 driver id
|
||||
private UUID cduDriverUuid; // ERP 에서는 uuid 로 호출
|
||||
|
||||
// 목표 (MIS 08:00 동기화)
|
||||
private BigDecimal cduTargetQty; // 목표량
|
||||
private Integer cduTargetVisitCnt; // 목표 방문수
|
||||
private LocalDateTime cduTargetSyncedAt; // 동기화 시각 (서버 set 가능)
|
||||
|
||||
// 실측 (드라이버 포탈/MIS 언로딩 입력)
|
||||
private BigDecimal cduActualQty; // 실제 언로딩량
|
||||
private BigDecimal cduActualWeight; // 무게 변환값
|
||||
private LocalDateTime cduUnloadedAt; // 언로딩 입력 시각
|
||||
private String cduUnloadPhoto; // 언로딩 사진 경로
|
||||
private String cduNote; // 메모
|
||||
|
||||
// 감사 컬럼 (MIS memberId → employee_external_map)
|
||||
private String cduExternalCreatedBy; // MIS memberId → employee_external_map 참조
|
||||
private String cduExternalUpdatedBy; // MIS memberId → employee_external_map 참조
|
||||
private String cduCreatedBy; // 생성자 ID
|
||||
private String cduUpdatedBy; // 수정자 ID
|
||||
private String cduLoginUser; // 로그인 User (CreatedBy/UpdatedBy 용)
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
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;
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CustomerDailyUcoResponseDto {
|
||||
|
||||
private UUID cduUuid; // UUID
|
||||
private LocalDate cduDate; // 대상 날짜 (YYYY-MM-DD)
|
||||
private Long cduDriverId; // Driver ID
|
||||
private BigDecimal cduTargetQty; // 목표량
|
||||
private Integer cduTargetVisitCnt; // 목표 방문수
|
||||
private LocalDateTime cduTargetSyncedAt; // MIS 목표량 동기화 시각
|
||||
private BigDecimal cduActualQty; // 실제 언로딩량
|
||||
private BigDecimal cduActualWeight; // 무게 변환값
|
||||
private LocalDateTime cduUnloadedAt; // 언로딩 입력 시각
|
||||
private String cduUnloadPhoto; // 언로딩 사진 경로
|
||||
private String cduNote; // 메모
|
||||
private LocalDateTime cduCreatedAt; // 생성 시간
|
||||
private String cduCreatedBy; // 생성자
|
||||
private LocalDateTime cduUpdatedAt; // 수정 시간
|
||||
private String cduUpdatedBy; // 수정자
|
||||
}
|
||||
|
|
@ -34,4 +34,10 @@ public class DriverDailyStatResponseDto {
|
|||
private LocalDateTime ddsPausedAt; // 일시정지
|
||||
private LocalDateTime ddsEndAt; // 운행 종료
|
||||
private String ddsEndReason; // 종료 사유
|
||||
|
||||
private BigDecimal ddsTargetQty; // 목표량 (8시 고정, % 분모)
|
||||
private Integer ddsTargetCount; // 목표 방문수 (8시 고정)
|
||||
private BigDecimal ddsActualQty; // 실제 언로딩량 (NULL = 미입력)
|
||||
private BigDecimal ddsActualWeight; // 무게 변환값 (NULL = 미입력)
|
||||
private LocalDateTime ddsUnloadedAt; // 언로딩 입력 시각 (NULL = 미입력)
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
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 java.util.UUID;
|
||||
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EntityListeners;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(schema = "crm", name = "customer_daily_uco")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
public class CustomerDailyUco {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long cduId;
|
||||
private UUID cduUuid;
|
||||
private LocalDate cduDate;
|
||||
private Long cduDriverId;
|
||||
private BigDecimal cduTargetQty; // 목표량
|
||||
private Integer cduTargetVisitCnt; // 목표 방문수
|
||||
private LocalDateTime cduTargetSyncedAt; // MIS 목표량 동기화 시각
|
||||
private BigDecimal cduActualQty; // 실제 언로딩량
|
||||
private BigDecimal cduActualWeight; // 무게로 변환
|
||||
private LocalDateTime cduUnloadedAt; // 언로딩 입력 시각
|
||||
|
||||
@Column(length = 255)
|
||||
private String cduUnloadPhoto;
|
||||
private String cduNote;
|
||||
private LocalDateTime cduCreatedAt;
|
||||
|
||||
@Column(name = "cdu_created_by")
|
||||
private String cduCreatedBy;
|
||||
private LocalDateTime cduUpdatedAt;
|
||||
|
||||
@Column(name = "cdu_updated_by")
|
||||
private String cduUpdatedBy;
|
||||
}
|
||||
|
|
@ -38,11 +38,17 @@ public class DriverDailyStat {
|
|||
private String ddsMin; // 운행시간 (뷰에서 텍스트, 예: "198.00 min")
|
||||
private Integer ddsEvent; // 안전 이벤트 수
|
||||
private BigDecimal ddsTotalQty; // 실제 픽업량 합계
|
||||
private BigDecimal ddsPlannedQty; // 예상 픽업량 합계 (목표)
|
||||
private BigDecimal ddsPlannedQty; // 예상 픽업량 합계
|
||||
private Long ddsCompletedCount; // 방문 완료(F) 수
|
||||
private Long ddsPlannedCount; // 전체 방문 목표 수
|
||||
private LocalDateTime ddsStartAt; // 운행 시작
|
||||
private LocalDateTime ddsPausedAt; // 일시정지
|
||||
private LocalDateTime ddsEndAt; // 운행 종료
|
||||
private String ddsEndReason; // 종료 사유
|
||||
|
||||
private BigDecimal ddsTargetQty; // 목표량 (8시 MIS 스냅샷, 고정 분모)
|
||||
private Integer ddsTargetCount; // 목표 방문수 (8시 고정)
|
||||
private BigDecimal ddsActualQty; // 실제 언로딩량 (NULL = 미입력)
|
||||
private BigDecimal ddsActualWeight; // 무게 변환값 (NULL = 미입력)
|
||||
private LocalDateTime ddsUnloadedAt; // 언로딩 입력 시각 (NULL = 미입력)
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package com.goi.erp.repository;
|
||||
|
||||
import com.goi.erp.entity.CustomerDailyUco;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface CustomerDailyUcoRepository extends JpaRepository<CustomerDailyUco, Long> {
|
||||
|
||||
// ── 조회 (대시보드 / 드릴다운) ──
|
||||
List<CustomerDailyUco> findByCduDate(LocalDate cduDate);
|
||||
|
||||
Optional<CustomerDailyUco> findByCduDriverIdAndCduDate(Long cduDriverId, LocalDate cduDate);
|
||||
|
||||
List<CustomerDailyUco> findByCduDriverIdAndCduDateBetween(
|
||||
Long cduDriverId, LocalDate from, LocalDate to);
|
||||
|
||||
// ── MIS 08:00 목표 upsert (목표 컬럼만 갱신, 실측값 보존) ──
|
||||
@Modifying
|
||||
@Query(value = """
|
||||
INSERT INTO crm.customer_daily_uco
|
||||
(cdu_date, cdu_driver_id,
|
||||
cdu_target_qty, cdu_target_visit_cnt, cdu_target_synced_at,
|
||||
cdu_created_by, cdu_updated_by)
|
||||
VALUES
|
||||
(:date, :driverId,
|
||||
:targetQty, :targetVisitCnt, :syncedAt,
|
||||
:loginUser, :loginUser)
|
||||
ON CONFLICT (cdu_driver_id, cdu_date)
|
||||
DO UPDATE SET
|
||||
cdu_target_qty = EXCLUDED.cdu_target_qty,
|
||||
cdu_target_visit_cnt = EXCLUDED.cdu_target_visit_cnt,
|
||||
cdu_target_synced_at = EXCLUDED.cdu_target_synced_at,
|
||||
cdu_updated_by = EXCLUDED.cdu_updated_by,
|
||||
cdu_updated_at = CURRENT_TIMESTAMP
|
||||
""", nativeQuery = true)
|
||||
int upsertTarget(@Param("date") LocalDate date,
|
||||
@Param("driverId") Long driverId,
|
||||
@Param("targetQty") BigDecimal targetQty,
|
||||
@Param("targetVisitCnt") Integer targetVisitCnt,
|
||||
@Param("syncedAt") LocalDateTime syncedAt,
|
||||
@Param("loginUser") String loginUser);
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
package com.goi.erp.service;
|
||||
|
||||
import com.goi.erp.dto.CustomerDailyUcoRequestDto;
|
||||
import com.goi.erp.dto.CustomerDailyUcoResponseDto;
|
||||
import com.goi.erp.entity.CustomerDailyUco;
|
||||
import com.goi.erp.repository.CustomerDailyUcoRepository;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CustomerDailyUcoService {
|
||||
|
||||
private final CustomerDailyUcoRepository customerDailyUcoRepository;
|
||||
private final HcmEmployeeClient hcmEmployeeClient;
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<CustomerDailyUcoResponseDto> getByDate(LocalDate date) {
|
||||
return customerDailyUcoRepository.findByCduDate(date).stream()
|
||||
.map(this::toResponseDto)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public CustomerDailyUcoResponseDto getByDriverAndDate(Long driverId, LocalDate date) {
|
||||
return customerDailyUcoRepository.findByCduDriverIdAndCduDate(driverId, date)
|
||||
.map(this::toResponseDto)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/** MIS 08:00 목표 동기화 — 목표 컬럼만 upsert, 실측값은 보존 */
|
||||
@Transactional
|
||||
public void syncTarget(CustomerDailyUcoRequestDto dto) {
|
||||
Long driverId = resolveDriverId(dto);
|
||||
LocalDateTime syncedAt = dto.getCduTargetSyncedAt() != null
|
||||
? dto.getCduTargetSyncedAt() : LocalDateTime.now();
|
||||
|
||||
customerDailyUcoRepository.upsertTarget(
|
||||
dto.getCduDate(), driverId,
|
||||
dto.getCduTargetQty(), dto.getCduTargetVisitCnt(), syncedAt,
|
||||
dto.getCduLoginUser());
|
||||
}
|
||||
|
||||
/** 드라이버 언로딩 입력 — 기존(목표) 행에 실측/사진 갱신, 없으면 신규 생성 */
|
||||
@Transactional
|
||||
public CustomerDailyUcoResponseDto recordUnload(CustomerDailyUcoRequestDto dto) {
|
||||
Long driverId = resolveDriverId(dto);
|
||||
LocalDateTime unloadedAt = dto.getCduUnloadedAt() != null
|
||||
? dto.getCduUnloadedAt() : LocalDateTime.now();
|
||||
|
||||
CustomerDailyUco entity = customerDailyUcoRepository
|
||||
.findByCduDriverIdAndCduDate(driverId, dto.getCduDate())
|
||||
.orElseGet(() -> CustomerDailyUco.builder()
|
||||
.cduDate(dto.getCduDate())
|
||||
.cduDriverId(driverId)
|
||||
.cduCreatedBy(dto.getCduLoginUser())
|
||||
.build());
|
||||
|
||||
entity.setCduActualQty(dto.getCduActualQty());
|
||||
entity.setCduActualWeight(dto.getCduActualWeight());
|
||||
entity.setCduUnloadedAt(unloadedAt);
|
||||
entity.setCduUnloadPhoto(dto.getCduUnloadPhoto());
|
||||
if (dto.getCduNote() != null) {
|
||||
entity.setCduNote(dto.getCduNote());
|
||||
}
|
||||
entity.setCduUpdatedBy(dto.getCduLoginUser());
|
||||
entity.setCduUpdatedAt(LocalDateTime.now()); // UPDATE 시 DB default 안 걸리므로 명시
|
||||
|
||||
return toResponseDto(customerDailyUcoRepository.save(entity));
|
||||
}
|
||||
|
||||
private CustomerDailyUcoResponseDto toResponseDto(CustomerDailyUco e) {
|
||||
return CustomerDailyUcoResponseDto.builder()
|
||||
.cduUuid(e.getCduUuid())
|
||||
.cduDate(e.getCduDate())
|
||||
.cduDriverId(e.getCduDriverId())
|
||||
.cduTargetQty(e.getCduTargetQty())
|
||||
.cduTargetVisitCnt(e.getCduTargetVisitCnt())
|
||||
.cduTargetSyncedAt(e.getCduTargetSyncedAt())
|
||||
.cduActualQty(e.getCduActualQty())
|
||||
.cduActualWeight(e.getCduActualWeight())
|
||||
.cduUnloadedAt(e.getCduUnloadedAt())
|
||||
.cduUnloadPhoto(e.getCduUnloadPhoto())
|
||||
.cduNote(e.getCduNote())
|
||||
.cduCreatedAt(e.getCduCreatedAt())
|
||||
.cduCreatedBy(e.getCduCreatedBy())
|
||||
.cduUpdatedAt(e.getCduUpdatedAt())
|
||||
.cduUpdatedBy(e.getCduUpdatedBy())
|
||||
.build();
|
||||
}
|
||||
|
||||
private Long resolveDriverId(CustomerDailyUcoRequestDto dto) {
|
||||
|
||||
// 1. driver id 직접 전달
|
||||
if (dto.getCduDriverId() != null) {
|
||||
return dto.getCduDriverId();
|
||||
}
|
||||
|
||||
// 2. MIS → externalId (UCO 는 항상 MIS_DRIVER)
|
||||
if (dto.getCduExternalDriverId() != null) {
|
||||
Map<String, Object> body =
|
||||
hcmEmployeeClient.getEmpFromExternalId("MIS_DRIVER", dto.getCduExternalDriverId());
|
||||
|
||||
if (body != null && body.get("eexEmpId") != null) {
|
||||
Object raw = body.get("eexEmpId");
|
||||
if (raw instanceof Number) {
|
||||
return ((Number) raw).longValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. ERP 내부 요청 (driverUuid 방식)
|
||||
if (dto.getCduDriverUuid() != null) {
|
||||
Map<String, Object> body = hcmEmployeeClient.getEmpFromUuid(dto.getCduDriverUuid());
|
||||
if (body != null && body.get("empId") != null) {
|
||||
Object raw = body.get("empId");
|
||||
if (raw instanceof Number) {
|
||||
return ((Number) raw).longValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -92,6 +92,11 @@ public class DriverDailyStatService {
|
|||
.ddsPausedAt(s.getDdsPausedAt())
|
||||
.ddsEndAt(s.getDdsEndAt())
|
||||
.ddsEndReason(s.getDdsEndReason())
|
||||
.ddsTargetQty(s.getDdsTargetQty())
|
||||
.ddsTargetCount(s.getDdsTargetCount())
|
||||
.ddsActualQty(s.getDdsActualQty())
|
||||
.ddsActualWeight(s.getDdsActualWeight())
|
||||
.ddsUnloadedAt(s.getDdsUnloadedAt())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue