[Config] Added

This commit is contained in:
Hyojin Ahn 2026-01-12 12:25:43 -05:00
parent 05f068276d
commit 62d453b926
24 changed files with 519 additions and 1555 deletions

View File

@ -0,0 +1,11 @@
package com.goi.erp.common.enums;
public enum ConfigDataType {
STRING,
INT,
DECIMAL,
BOOL,
DATE,
DATETIME,
JSON
}

View File

@ -0,0 +1,89 @@
package com.goi.erp.controller;
import com.goi.erp.dto.ConfigRequestDto;
import com.goi.erp.dto.ConfigResponseDto;
import com.goi.erp.entity.ConfigChangeLog;
import com.goi.erp.service.ConfigService;
import com.goi.erp.repository.ConfigChangeLogRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/config")
@RequiredArgsConstructor
public class ConfigController {
private final ConfigService configService;
private final ConfigChangeLogRepository configChangeLogRepository;
/* ===============================
READ
=============================== */
/**
* 단일 config 조회
* GET /configs/{module}/{key}
*/
@GetMapping("/{module}/{key}")
public ResponseEntity<ConfigResponseDto> getConfig(
@PathVariable String module,
@PathVariable String key
) {
return ResponseEntity.ok(
configService.getOne(module, key)
);
}
/**
* 모듈별 config 목록
* GET /configs/{module}
*/
@GetMapping("/{module}")
public ResponseEntity<List<ConfigResponseDto>> getConfigsByModule(
@PathVariable String module
) {
return ResponseEntity.ok(
configService.getAllByModule(module)
);
}
/* ===============================
CREATE / UPDATE
=============================== */
@PostMapping
public ResponseEntity<ConfigResponseDto> save(
@RequestBody ConfigRequestDto dto
) {
return ResponseEntity.ok(
configService.save(dto)
);
}
/* ===============================
CHANGE HISTORY
=============================== */
/**
* config 변경 이력
* GET /configs/{module}/{key}/history
*/
@GetMapping("/{module}/{key}/history")
public ResponseEntity<List<ConfigChangeLog>> getHistory(
@PathVariable String module,
@PathVariable String key
) {
return ResponseEntity.ok(
configChangeLogRepository
.findAllByCclModuleAndCclKeyOrderByCclChangedAtDesc(
module, key
)
);
}
}

View File

@ -1,208 +0,0 @@
package com.goi.erp.controller;
import com.goi.erp.common.permission.PermissionChecker;
import com.goi.erp.common.permission.PermissionSet;
import com.goi.erp.dto.CustomerDailyOrderResponseDto;
import com.goi.erp.dto.CustomerRequestDto;
import com.goi.erp.dto.CustomerResponseDto;
import com.goi.erp.service.CustomerDailyOrderService;
import com.goi.erp.service.CustomerService;
import com.goi.erp.token.PermissionAuthenticationToken;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpStatus;
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.UUID;
@RestController
@RequestMapping("/customer")
@RequiredArgsConstructor
public class CustomerController {
@Value("${pagination.default-page:0}")
private int defaultPage;
@Value("${pagination.default-size:20}")
private int defaultSize;
@Value("${pagination.max-size:100}")
private int maxSize;
private final CustomerService customerService;
private final CustomerDailyOrderService dailyOrderService;
// CREATE
@PostMapping
public ResponseEntity<CustomerResponseDto> createCustomer(@RequestBody CustomerRequestDto requestDto) {
// 권한 체크
PermissionAuthenticationToken auth = (PermissionAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
if (auth == null || auth.getPermissionSet() == null) {
throw new AccessDeniedException("Permission information is missing");
}
PermissionSet permissionSet = auth.getPermissionSet();
if (!PermissionChecker.canCreateCRM(permissionSet)) {
throw new AccessDeniedException("You do not have permission to read all CRM data");
}
CustomerResponseDto responseDto = customerService.createCustomer(requestDto);
return new ResponseEntity<>(responseDto, HttpStatus.CREATED);
}
// READ ALL
@GetMapping
public ResponseEntity<Page<CustomerResponseDto>> getAllCustomers(
@RequestParam(required = false) Integer page,
@RequestParam(required = false) Integer size
) {
// 권한 체크
PermissionAuthenticationToken auth = (PermissionAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
if (auth == null || auth.getPermissionSet() == null) {
throw new AccessDeniedException("Permission information is missing");
}
PermissionSet permissionSet = auth.getPermissionSet();
if (!PermissionChecker.canReadCRMAll(permissionSet)) {
throw new AccessDeniedException("You do not have permission to read all CRM data");
}
//
int p = (page == null) ? defaultPage : page;
int s = (size == null) ? defaultSize : size;
if (s > maxSize) s = maxSize;
//
return ResponseEntity.ok(customerService.getAllCustomers(p, s));
}
// READ ONE
@GetMapping("/uuid/{uuid}")
public ResponseEntity<CustomerResponseDto> getCustomer(@PathVariable UUID uuid) {
// 권한 체크
PermissionAuthenticationToken auth = (PermissionAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
if (auth == null || auth.getPermissionSet() == null) {
throw new AccessDeniedException("Permission information is missing");
}
PermissionSet permissionSet = auth.getPermissionSet();
if (!PermissionChecker.canReadCRM(permissionSet)) {
throw new AccessDeniedException("You do not have permission to read all CRM data");
}
return ResponseEntity.ok(customerService.getCustomerByUuid(uuid));
}
// UPDATE
@PatchMapping("/uuid/{uuid}")
public ResponseEntity<CustomerResponseDto> updateCustomer(
@PathVariable UUID uuid,
@RequestBody CustomerRequestDto requestDto) {
// 권한 체크
PermissionAuthenticationToken auth = (PermissionAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
if (auth == null || auth.getPermissionSet() == null) {
throw new AccessDeniedException("Permission information is missing");
}
PermissionSet permissionSet = auth.getPermissionSet();
if (!PermissionChecker.canUpdateCRM(permissionSet)) {
throw new AccessDeniedException("You do not have permission to read all CRM data");
}
return ResponseEntity.ok(customerService.updateCustomer(uuid, requestDto));
}
// DELETE
@DeleteMapping("/uuid/{uuid}")
public ResponseEntity<Void> deleteCustomer(@PathVariable UUID uuid) {
// 권한 체크
PermissionAuthenticationToken auth = (PermissionAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
if (auth == null || auth.getPermissionSet() == null) {
throw new AccessDeniedException("Permission information is missing");
}
PermissionSet permissionSet = auth.getPermissionSet();
if (!PermissionChecker.canDeleteCRM(permissionSet)) {
throw new AccessDeniedException("You do not have permission to read all CRM data");
}
customerService.deleteCustomer(uuid);
return ResponseEntity.noContent().build();
}
// from MIS
@GetMapping("/no/{cusNo}")
public ResponseEntity<CustomerResponseDto> getCustomer(@PathVariable String cusNo) {
// 권한 체크
PermissionAuthenticationToken auth = (PermissionAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
if (auth == null || auth.getPermissionSet() == null) {
throw new AccessDeniedException("Permission information is missing");
}
PermissionSet permissionSet = auth.getPermissionSet();
if (!PermissionChecker.canDeleteCRM(permissionSet)) {
throw new AccessDeniedException("You do not have permission to read all CRM data");
}
//
CustomerResponseDto customer = customerService.getCustomerByNo(cusNo);
return ResponseEntity.ok(customer);
}
@PatchMapping("/no/{cusNo}")
public ResponseEntity<CustomerResponseDto> updateCustomer(@PathVariable String cusNo,
@RequestBody CustomerRequestDto dto) {
// 권한 체크
PermissionAuthenticationToken auth = (PermissionAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
if (auth == null || auth.getPermissionSet() == null) {
throw new AccessDeniedException("Permission information is missing");
}
PermissionSet permissionSet = auth.getPermissionSet();
if (!PermissionChecker.canDeleteCRM(permissionSet)) {
throw new AccessDeniedException("You do not have permission to read all CRM data");
}
//
CustomerResponseDto updated = customerService.updateCustomerByNo(cusNo, dto);
return ResponseEntity.ok(updated);
}
// READ DAILY ORDER BY CUSTOMER NO + DATE
@GetMapping("/no/{cusNo}/daily-orders/{orderDate}")
public ResponseEntity<CustomerDailyOrderResponseDto> getDailyOrderByCustomerNo(
@PathVariable String cusNo,
@PathVariable @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate orderDate
) {
PermissionAuthenticationToken auth =
(PermissionAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
if (auth == null || auth.getPermissionSet() == null) {
throw new AccessDeniedException("Permission information is missing");
}
PermissionSet permissionSet = auth.getPermissionSet();
if (!PermissionChecker.canReadCRM(permissionSet)) {
throw new AccessDeniedException("You do not have permission to read daily order");
}
return ResponseEntity.ok(
dailyOrderService.getDailyOrderByCustomerNo(cusNo, orderDate)
);
}
}

View File

@ -1,165 +0,0 @@
package com.goi.erp.controller;
import com.goi.erp.common.permission.PermissionChecker;
import com.goi.erp.common.permission.PermissionSet;
import com.goi.erp.dto.CustomerDailyOrderRequestDto;
import com.goi.erp.dto.CustomerDailyOrderResponseDto;
import com.goi.erp.service.CustomerDailyOrderService;
import com.goi.erp.token.PermissionAuthenticationToken;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpStatus;
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.UUID;
@RestController
@RequestMapping("/customer-daily-order")
@RequiredArgsConstructor
public class CustomerDailyOrderController {
@Value("${pagination.default-page:0}")
private int defaultPage;
@Value("${pagination.default-size:20}")
private int defaultSize;
@Value("${pagination.max-size:100}")
private int maxSize;
private final CustomerDailyOrderService dailyOrderService;
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();
}
// CREATE
@PostMapping
public ResponseEntity<CustomerDailyOrderResponseDto> createDailyOrder(
@RequestBody CustomerDailyOrderRequestDto dto) {
PermissionSet permissions = getPermission();
if (!PermissionChecker.canCreateCRM(permissions)) {
throw new AccessDeniedException("You do not have permission to create daily orders");
}
CustomerDailyOrderResponseDto created = dailyOrderService.createDailyOrder(dto);
return new ResponseEntity<>(created, HttpStatus.CREATED);
}
// READ ALL (paged)
@GetMapping
public ResponseEntity<Page<CustomerDailyOrderResponseDto>> getAllDailyOrders(
@RequestParam(required = false) Integer page,
@RequestParam(required = false) Integer size) {
PermissionSet permissions = getPermission();
if (!PermissionChecker.canReadCRMAll(permissions)) {
throw new AccessDeniedException("You do not have permission to read all daily orders");
}
int p = (page == null) ? defaultPage : page;
int s = (size == null) ? defaultSize : size;
if (s > maxSize) s = maxSize;
return ResponseEntity.ok(dailyOrderService.getAllDailyOrders(p, s));
}
// READ ONE BY UUID
@GetMapping("/{uuid}")
public ResponseEntity<CustomerDailyOrderResponseDto> getDailyOrder(@PathVariable UUID uuid) {
PermissionSet permissions = getPermission();
if (!PermissionChecker.canReadCRM(permissions)) {
throw new AccessDeniedException("You do not have permission to read daily order");
}
return ResponseEntity.ok(dailyOrderService.getDailyOrderByUuid(uuid));
}
// READ BY CUSTOMER + DATE
@GetMapping("/customer/{customerNo}/{orderDate}")
public ResponseEntity<CustomerDailyOrderResponseDto> getDailyOrderByCustomerNo(
@PathVariable String customerNo,
@PathVariable @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate orderDate
)
{
PermissionSet permissions = getPermission();
if (!PermissionChecker.canReadCRM(permissions)) {
throw new AccessDeniedException("You do not have permission to read daily order");
}
return ResponseEntity.ok(dailyOrderService.getDailyOrderByCustomerNo(customerNo, orderDate));
}
// UPDATE BY CUSTOMER + DATE
@PatchMapping("/customer/{customerNo}/{orderDate}")
public ResponseEntity<CustomerDailyOrderResponseDto> updateDailyOrderByCustomerNo(
@PathVariable String customerNo,
@PathVariable @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate orderDate,
@RequestBody CustomerDailyOrderRequestDto dto
) {
PermissionSet permissions = getPermission();
if (!PermissionChecker.canUpdateCRM(permissions)) {
throw new AccessDeniedException("You do not have permission to update daily order");
}
return ResponseEntity.ok(
dailyOrderService.updateDailyOrderByCustomerNoAndOrderDate(customerNo, orderDate, dto)
);
}
// UPDATE
@PatchMapping("/{uuid}")
public ResponseEntity<CustomerDailyOrderResponseDto> updateDailyOrder(
@PathVariable UUID uuid,
@RequestBody CustomerDailyOrderRequestDto dto) {
PermissionSet permissions = getPermission();
if (!PermissionChecker.canUpdateCRM(permissions)) {
throw new AccessDeniedException("You do not have permission to update daily order");
}
return ResponseEntity.ok(dailyOrderService.updateDailyOrder(uuid, dto));
}
// DELETE
@DeleteMapping("/{uuid}")
public ResponseEntity<Void> deleteDailyOrder(@PathVariable UUID uuid) {
PermissionSet permissions = getPermission();
if (!PermissionChecker.canDeleteCRM(permissions)) {
throw new AccessDeniedException("You do not have permission to delete daily order");
}
dailyOrderService.deleteDailyOrder(uuid);
return ResponseEntity.noContent().build();
}
}

View File

@ -0,0 +1,18 @@
package com.goi.erp.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ConfigRequestDto {
private String cfgModule; // SYS / OPR / DISPATCH ...
private String cfgKey;
private String cfgValue;
private String cfgDataType; // STRING / INT / DECIMAL / BOOL / DATE / DATETIME / JSON
}

View File

@ -0,0 +1,30 @@
package com.goi.erp.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.UUID;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ConfigResponseDto {
private Long cfgId;
private UUID cfgUuid;
private String cfgModule;
private String cfgKey;
private String cfgValue;
private String cfgDataType;
private LocalDateTime cfgCreatedAt;
private LocalDateTime cfgUpdatedAt;
private String cfgCreatedBy;
private String cfgUpdatedBy;
}

View File

@ -1,44 +0,0 @@
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 CustomerDailyOrderRequestDto {
private LocalDate cdoOrderDate; // 주문 날짜 (YYYY-MM-DD)
private String cdoOrderType; // 주문 타입 ('N' )
private String cdoRequestNote; // 주문 요청 메모
private String cdoExternalDriverId; // MIS 에서 driverId 호출해서 employee_external_map 참조해야함
private Long cdoDriverId; // 배정된 driver id
private UUID cdoDriverUuid; // ERP 에서는 uuid 호출
private Long cdoCustomerId; // 고객 ID
private String cdoCustomerNo;
private UUID cdoCustomerUuid;
private String cdoPaymentType; // 결제 타입
private String cdoCycle; // Cycle (방문 주기)
private BigDecimal cdoRate; // 요율 (리터당 가격 )
private String cdoCreatedBy; // 생성자 ID
private String cdoUpdatedBy; // 수정자 ID
private String cdoStatus; // 상태 ('A', 'F', 'D')
private String cdoVisitFlag; // 방문 여부 Flag
private BigDecimal cdoEstimatedQty; // 예상 수거량
private BigDecimal cdoQuantity; // 실제 수거량
private Integer cdoSludge; // Sludge 여부/수치
private String cdoPayStatus; // 결제 여부 ('N', 'Y')
private BigDecimal cdoPayAmount; // 결제 금액
private String cdoPayeeName; // Payee 이름
private String cdoPayeeSign; // Payee 사인 (이미지 경로 )
private LocalDateTime cdoPickupAt; // 입력 시간
private String cdoPickupNote; // 주문 요청 메모
private BigDecimal cdoPickupLat; // 픽업 위도
private BigDecimal cdoPickupLon; // 픽업 경도
private Integer cdoPickupMin; // 픽업 작업 시간()
private String cdoLoginUser; // 로그인한 User (CreatedBy/UpdatedBy )
}

View File

@ -1,47 +0,0 @@
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 CustomerDailyOrderResponseDto {
private UUID cdoUuid; // UUID
private LocalDate cdoOrderDate; // YYYYMMDD
private String cdoOrderType; // 주문 타입
private String cdoRequestNote; // 요청 메모
//private Long cdoDriverId; // Driver ID
//private Long cdoCustomerId; //
private String cdoCustomerNo;
private String cdoPaymentType; // 결제 방식
private String cdoCycle; // Cycle
private BigDecimal cdoRate; // 요율
private LocalDateTime cdoCreatedAt; // 생성 시간
private String cdoCreatedBy; // 생성자
private LocalDateTime cdoUpdatedAt; // 수정 시간
private String cdoUpdatedBy; // 수정자
private String cdoStatus; // 상태
private String cdoVisitFlag; // 방문 여부
private BigDecimal cdoEstimatedQty; // 예상 수거량
private BigDecimal cdoQuantity; // 실제 수거량
private Integer cdoSludge; // Sludge
private String cdoPayStatus; // 결제 상태
private BigDecimal cdoPayAmount; // 결제 금액
private String cdoPayeeName; // Payee 이름
private String cdoPayeeSign; // Payee 사인
private LocalDateTime cdoPickupAt; // 입력 시간
private String cdoPickupNote; // 요청 메모
private BigDecimal cdoPickupLat; // 픽업 위도
private BigDecimal cdoPickupLon; // 픽업 경도
private Integer cdoPickupMin; // 픽업 시간()
}

View File

@ -1,56 +0,0 @@
package com.goi.erp.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDate;
@Data
@NoArgsConstructor
public class CustomerRequestDto {
private String cusNo; // c_accountno
private String cusName; // c_name
private String cusStatus; // c_status ('A', 'I', 'D' )
private Long cusAreaId; // region / area mapping
private String cusAddress1; // c_address
private String cusAddress2; // c_mailingaddr or c_location
private String cusPostalCode; // c_postal
private String cusCity; // c_city
private String cusProvince; // c_province
private Double cusGeoLat; // c_geolat
private Double cusGeoLon; // c_geolon
private String cusEmail; // c_email
private String cusPhone; // c_phone
private String cusPhoneExt; // c_phoneext
private LocalDate cusContractDate; // c_contractdate
private String cusContractedBy; // c_contractby
private LocalDate cusInstallDate; // c_installdate
private BigDecimal cusFullCycle; // c_fullcycle
private Boolean cusFullCycleFlag; // c_fullcycleflag
private BigDecimal cusFullCycleForced; // c_fullcycleforced
private LocalDate cusFullCycleForcedDate; // c_forceddate
private String cusSchedule; // c_schedule
private String cusScheduledays; // c_scheduleday
private LocalDate cusLastPaidDate; // c_lastpaiddate
private Double cusRate; // c_rate
private String cusPayMethod; // c_paymenttype
private String cusAccountNo; // c_accountno
private LocalDate cusIsccDate; // c_form_eu
private LocalDate cusCorsiaDate; // c_form_corsia
private String cusHstNo; // c_hstno
private LocalDate cusTerminatedDate;
private String cusTerminationReason;
private String cusInstallLocation;
private LocalDate cusLastPickupDate;
private Integer cusLastPickupQty;
private Double cusLastPickupLat;
private Double cusLastPickupLon;
private String cusOpenTime;
private String cusComment; // c_comment_ri
private String cusContactComment; // c_comment_ci
private Integer cusLastPickupMin;
private String cusLoginUser;
}

View File

@ -1,57 +0,0 @@
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.util.UUID;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CustomerResponseDto {
private UUID cusUuid;
private String cusNo;
private String cusName;
private String cusStatus;
private String cusAddress1;
private String cusAddress2;
private String cusCity;
private String cusProvince;
private BigDecimal cusGeoLat;
private BigDecimal cusGeoLon;
private String cusEmail;
private String cusPhone;
private String cusPhoneExt;
private LocalDate cusContractDate;
private String cusContractedBy;
private LocalDate cusInstallDate;
private String cusInstallLocation;
private LocalDate cusLastPickupDate;
private Integer cusLastPickupQty;
private BigDecimal cusLastPickupLat;
private BigDecimal cusLastPickupLon;
private BigDecimal cusFullCycle;
private Boolean cusFullCycleFlag;
private BigDecimal cusFullCycleForced;
private LocalDate cusFullCycleForcedDate;
private String cusSchedule;
private String cusScheduledays;
private LocalDate cusLastPaidDate;
private BigDecimal cusRate;
private String cusPayMethod;
private String cusAccountNo;
private LocalDate cusIsccDate;
private LocalDate cusCorsiaDate;
private String cusHstNo;
private LocalDate cusTerminatedDate;
private String cusTerminationReason;
private Integer cusLastPickupMin;
private String cusOpenTime;
private String cusComment;
private String cusContactComment;
}

View File

@ -0,0 +1,74 @@
package com.goi.erp.entity;
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;
import jakarta.persistence.UniqueConstraint;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.UUID;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@Entity
@Table(
name = "config",
uniqueConstraints = {
@UniqueConstraint(
name = "uk_config_module_key",
columnNames = {"cfg_module", "cfg_key"}
)
}
)
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EntityListeners(AuditingEntityListener.class)
public class Config {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cfg_id")
private Long cfgId;
@Column(name = "cfg_uuid", nullable = false)
private UUID cfgUuid;
@Column(name = "cfg_module", nullable = false, length = 50)
private String cfgModule;
@Column(name = "cfg_key", nullable = false, length = 100)
private String cfgKey;
@Column(name = "cfg_value", columnDefinition = "TEXT")
private String cfgValue;
@Column(name = "cfg_data_type", length = 10)
private String cfgDataType;
@Column(name = "cfg_created_at")
private LocalDateTime cfgCreatedAt;
@CreatedBy
@Column(name = "cfg_created_by", length = 50)
private String cfgCreatedBy;
@Column(name = "cfg_updated_at")
private LocalDateTime cfgUpdatedAt;
@LastModifiedBy
@Column(name = "cfg_updated_by", length = 50)
private String cfgUpdatedBy;
}

View File

@ -0,0 +1,54 @@
package com.goi.erp.entity;
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;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.UUID;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@Entity
@Table(name = "config_change_log")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EntityListeners(AuditingEntityListener.class)
public class ConfigChangeLog {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long cclId;
@Column(nullable = false, unique = true)
private UUID cclUuid;
@Column(nullable = false, length = 50)
private String cclModule;
@Column(nullable = false, length = 100)
private String cclKey;
@Column(columnDefinition = "TEXT")
private String cclOldValue;
@Column(columnDefinition = "TEXT")
private String cclNewValue;
private LocalDateTime cclChangedAt;
@CreatedBy
private String cclChangedBy;
}

View File

@ -1,93 +0,0 @@
package com.goi.erp.entity;
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;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.UUID;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@Entity
@Table(name = "customer")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EntityListeners(AuditingEntityListener.class)
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long cusId;
@Column(nullable = false, unique = true)
private UUID cusUuid;
@Column(length = 50)
private String cusNo;
@Column(nullable = false)
private String cusName;
@Column(length = 1)
private String cusStatus;
private Long cusAreaId;
private String cusAddress1;
private String cusAddress2;
private String cusPostalCode;
private String cusCity;
private String cusProvince;
private BigDecimal cusGeoLat;
private BigDecimal cusGeoLon;
private String cusEmail;
private String cusPhone;
private String cusPhoneExt;
private String cusOpenTime;
private String cusComment;
private String cusContactComment;
private LocalDate cusContractDate;
private String cusContractedBy;
private LocalDate cusInstallDate;
private String cusInstallLocation;
private LocalDate cusLastPickupDate;
private Integer cusLastPickupQty;
private BigDecimal cusLastPickupLat;
private BigDecimal cusLastPickupLon;
private BigDecimal cusFullCycle;
private Boolean cusFullCycleFlag;
private BigDecimal cusFullCycleForced;
private LocalDate cusFullCycleForcedDate;
private String cusSchedule;
private String cusScheduledays;
private LocalDate cusLastPaidDate;
private BigDecimal cusRate;
private String cusPayMethod;
private String cusAccountNo;
private LocalDate cusIsccDate;
private LocalDate cusCorsiaDate;
private String cusHstNo;
private LocalDate cusTerminatedDate;
private String cusTerminationReason;
private Integer cusLastPickupMin;
@CreatedBy
private String cusCreatedBy;
@LastModifiedBy
private String cusUpdatedBy;
}

View File

@ -1,107 +0,0 @@
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.annotation.CreatedBy;
import org.springframework.data.annotation.LastModifiedBy;
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(name = "customer_daily_order")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EntityListeners(AuditingEntityListener.class)
public class CustomerDailyOrder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long cdoId;
private UUID cdoUuid;
private LocalDate cdoOrderDate;
@Column(length = 1)
private String cdoOrderType;
private String cdoRequestNote;
private Long cdoDriverId;
private Long cdoCustomerId;
private String cdoCustomerNo;
@Column(length = 10)
private String cdoPaymentType;
@Column(length = 1)
private String cdoCycle;
private BigDecimal cdoRate;
private LocalDateTime cdoCreatedAt;
@CreatedBy
@Column(name = "cdo_created_by")
private String cdoCreatedBy;
private LocalDateTime cdoUpdatedAt;
@LastModifiedBy
@Column(name = "cdo_updated_by")
private String cdoUpdatedBy;
@Column(length = 1)
private String cdoStatus;
@Column(length = 1)
private String cdoVisitFlag;
private LocalDateTime cdoPickupAt;
private String cdoPickupNote;
private BigDecimal cdoEstimatedQty;
private BigDecimal cdoQuantity;
private Integer cdoSludge;
@Column(length = 1)
private String cdoPayStatus;
private BigDecimal cdoPayAmount;
@Column(length = 200)
private String cdoPayeeName;
@Column(length = 200)
private String cdoPayeeSign;
@Column(precision = 10, scale = 7)
private BigDecimal cdoPickupLat;
@Column(precision = 10, scale = 7)
private BigDecimal cdoPickupLon;
private Integer cdoPickupMin;
}

View File

@ -1,37 +0,0 @@
package com.goi.erp.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "employee")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "emp_id")
private Long empId; // 내부 PK, 외부 노출 X
@Column(name = "emp_uuid", unique = true, nullable = false)
private UUID empUuid; // 외부 키로 사용
@Column(name = "emp_first_name")
private String empFirstName;
@Column(name = "emp_last_name")
private String empLastName;
}

View File

@ -1,65 +0,0 @@
package com.goi.erp.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.time.LocalDate;
import java.time.LocalDateTime;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.LastModifiedBy;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "entity_change_log")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class EntityChangeLog {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ecl_id")
private Long eclId;
@Column(name = "ecl_entity_type")
private String eclEntityType;
@Column(name = "ecl_entity_id")
private Long eclEntityId;
@Column(name = "ecl_field_name")
private String eclFieldName;
@Column(name = "ecl_column_name")
private String eclColumnName;
@Column(name = "ecl_old_value")
private String eclOldValue;
@Column(name = "ecl_new_value")
private String eclNewValue;
@Column(name = "ecl_effective_date")
private LocalDate eclEffectiveDate;
@LastModifiedBy
@Column(name = "ecl_changed_by")
private String eclChangedBy;
@Column(name = "ecl_changed_at")
private LocalDateTime eclChangedAt;
@CreatedBy
@Column(name = "ecl_created_by")
private String eclCreatedBy;
}

View File

@ -0,0 +1,20 @@
package com.goi.erp.repository;
import com.goi.erp.entity.ConfigChangeLog;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.UUID;
@Repository
public interface ConfigChangeLogRepository
extends JpaRepository<ConfigChangeLog, Long> {
List<ConfigChangeLog> findAllByCclModuleAndCclKeyOrderByCclChangedAtDesc(String cclModule, String cclKey);
List<ConfigChangeLog> findAllByCclModuleOrderByCclChangedAtDesc(String cclModule);
List<ConfigChangeLog> findAllByCclUuid(UUID cclUuid);
}

View File

@ -0,0 +1,22 @@
package com.goi.erp.repository;
import com.goi.erp.entity.Config;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@Repository
public interface ConfigRepository extends JpaRepository<Config, Long> {
Optional<Config> findByCfgUuid(UUID cfgUuid);
Optional<Config> findByCfgModuleAndCfgKey(String cfgModule, String cfgKey);
List<Config> findAllByCfgModule(String cfgModule);
boolean existsByCfgModuleAndCfgKey(String cfgModule, String cfgKey);
}

View File

@ -1,35 +0,0 @@
package com.goi.erp.repository;
import com.goi.erp.entity.CustomerDailyOrder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
import java.util.UUID;
import java.time.LocalDate;
import java.util.List;
@Repository
public interface CustomerDailyOrderRepository extends JpaRepository<CustomerDailyOrder, Integer> {
// 기본 페이징 조회
Page<CustomerDailyOrder> findAll(Pageable pageable);
// UUID 조회
Optional<CustomerDailyOrder> findByCdoUuid(UUID cdoUuid);
// 특정 고객의 특정 날짜 주문 조회
Optional<CustomerDailyOrder> findByCdoCustomerNoAndCdoOrderDate(String cdoCustomerNo, LocalDate cdoOrderDate);
// 특정 고객의 모든 주문 리스트
List<CustomerDailyOrder> findByCdoCustomerNo(String cdoCustomerNo);
// 특정 날짜의 전체 주문
List<CustomerDailyOrder> findByCdoOrderDate(LocalDate cdoOrderDate);
// 존재 여부 체크 (중복 입력 방지)
boolean existsByCdoCustomerNoAndCdoOrderDate(String cdoCustomerNo, LocalDate cdoOrderDate);
}

View File

@ -1,22 +0,0 @@
package com.goi.erp.repository;
import com.goi.erp.entity.Customer;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
import java.util.UUID;
@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
Page<Customer> findAll(Pageable pageable);
Optional<Customer> findByCusUuid(UUID cusUuid);
Optional<Customer> findByCusNo(String cusNo);
boolean existsByCusNo(String cusNo);
}

View File

@ -1,10 +0,0 @@
package com.goi.erp.repository;
import com.goi.erp.entity.EntityChangeLog;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface EntityChangeLogRepository extends JpaRepository<EntityChangeLog, Long> {
}

View File

@ -0,0 +1,201 @@
package com.goi.erp.service;
import com.goi.erp.dto.ConfigRequestDto;
import com.goi.erp.dto.ConfigResponseDto;
import com.goi.erp.entity.Config;
import com.goi.erp.entity.ConfigChangeLog;
import com.goi.erp.repository.ConfigChangeLogRepository;
import com.goi.erp.repository.ConfigRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
@Service
@RequiredArgsConstructor
public class ConfigService {
private final ConfigRepository configRepository;
private final ConfigChangeLogRepository configChangeLogRepository;
/* ===============================
READ
=============================== */
@Transactional(readOnly = true)
public Config getConfig(String module, String key) {
return configRepository.findByCfgModuleAndCfgKey(module, key)
.orElseThrow(() ->
new IllegalStateException(
"Config not found: " + module + "." + key
)
);
}
@Transactional(readOnly = true)
public ConfigResponseDto getOne(String module, String key) {
return toResponse(getConfig(module, key));
}
@Transactional(readOnly = true)
public List<ConfigResponseDto> getAllByModule(String module) {
return configRepository.findAllByCfgModule(module)
.stream()
.map(this::toResponse)
.toList();
}
@Transactional(readOnly = true)
public String getString(String module, String key) {
return getConfig(module, key).getCfgValue();
}
@Transactional(readOnly = true)
public Integer getInt(String module, String key) {
Config cfg = getConfig(module, key);
assertType(cfg, "INT");
return Integer.valueOf(cfg.getCfgValue());
}
@Transactional(readOnly = true)
public BigDecimal getDecimal(String module, String key) {
Config cfg = getConfig(module, key);
assertType(cfg, "DECIMAL");
return new BigDecimal(cfg.getCfgValue());
}
@Transactional(readOnly = true)
public Boolean getBoolean(String module, String key) {
Config cfg = getConfig(module, key);
assertType(cfg, "BOOL");
return Boolean.valueOf(cfg.getCfgValue());
}
@Transactional(readOnly = true)
public LocalDate getDate(String module, String key) {
Config cfg = getConfig(module, key);
assertType(cfg, "DATE");
return LocalDate.parse(cfg.getCfgValue());
}
@Transactional(readOnly = true)
public LocalDateTime getDateTime(String module, String key) {
Config cfg = getConfig(module, key);
assertType(cfg, "DATETIME");
return LocalDateTime.parse(cfg.getCfgValue());
}
/* ===============================
CREATE / UPDATE
=============================== */
@Transactional
public ConfigResponseDto save(ConfigRequestDto dto) {
validateValue(dto.getCfgDataType(), dto.getCfgValue());
// 있으면 update
Config existing = configRepository.findByCfgModuleAndCfgKey(dto.getCfgModule(), dto.getCfgKey()).orElse(null);
String oldValue = existing != null ? existing.getCfgValue() : null;
Config config = (existing != null)
? existing
: Config.builder()
.cfgUuid(UUID.randomUUID())
.cfgModule(dto.getCfgModule())
.cfgKey(dto.getCfgKey())
.build();
config.setCfgValue(dto.getCfgValue());
config.setCfgDataType(dto.getCfgDataType());
Config saved = configRepository.save(config);
/* ===============================
CHANGE LOG
=============================== */
if (existing != null &&
oldValue != null &&
!oldValue.equals(dto.getCfgValue())) {
configChangeLogRepository.save(
ConfigChangeLog.builder()
.cclUuid(UUID.randomUUID())
.cclModule(saved.getCfgModule())
.cclKey(saved.getCfgKey())
.cclOldValue(oldValue)
.cclNewValue(dto.getCfgValue())
.build()
);
}
return toResponse(saved);
}
/* ===============================
Validation
=============================== */
private void assertType(Config cfg, String expected) {
if (!expected.equalsIgnoreCase(cfg.getCfgDataType())) {
throw new IllegalStateException(
"Config type mismatch: " +
cfg.getCfgModule() + "." + cfg.getCfgKey() +
" expected=" + expected +
" actual=" + cfg.getCfgDataType()
);
}
}
private void validateValue(String type, String value) {
if (value == null) return;
try {
switch (type) {
case "INT" -> Integer.parseInt(value);
case "DECIMAL" -> new BigDecimal(value);
case "BOOL" -> Boolean.parseBoolean(value);
case "DATE" -> LocalDate.parse(value);
case "DATETIME" -> LocalDateTime.parse(value);
case "STRING", "JSON" -> {
/* no-op */
}
default -> throw new IllegalArgumentException(
"Unsupported cfgDataType: " + type
);
}
} catch (Exception e) {
throw new IllegalArgumentException(
"Invalid value for type " + type + ": " + value, e
);
}
}
/* ===============================
Mapper
=============================== */
private ConfigResponseDto toResponse(Config entity) {
return ConfigResponseDto.builder()
.cfgId(entity.getCfgId())
.cfgUuid(entity.getCfgUuid())
.cfgModule(entity.getCfgModule())
.cfgKey(entity.getCfgKey())
.cfgValue(entity.getCfgValue())
.cfgDataType(entity.getCfgDataType())
.cfgCreatedAt(entity.getCfgCreatedAt())
.cfgUpdatedAt(entity.getCfgUpdatedAt())
.cfgCreatedBy(entity.getCfgCreatedBy())
.cfgUpdatedBy(entity.getCfgUpdatedBy())
.build();
}
}

View File

@ -1,270 +0,0 @@
package com.goi.erp.service;
import com.goi.erp.dto.CustomerDailyOrderRequestDto;
import com.goi.erp.dto.CustomerDailyOrderResponseDto;
import com.goi.erp.entity.CustomerDailyOrder;
import com.goi.erp.repository.CustomerDailyOrderRepository;
import com.goi.erp.repository.CustomerRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.UUID;
@Service
@RequiredArgsConstructor
public class CustomerDailyOrderService {
private final CustomerDailyOrderRepository dailyOrderRepository;
private final CustomerRepository customerRepository;
private final HcmEmployeeClient hcmEmployeeClient;
/**
* CREATE
*/
public CustomerDailyOrderResponseDto createDailyOrder(CustomerDailyOrderRequestDto dto) {
// customer id
Long customerId = resolveCustomerId(dto);
// driver id (employee id)
Long driverId = resolveDriverId(dto);
CustomerDailyOrder order = CustomerDailyOrder.builder()
.cdoUuid(UUID.randomUUID())
.cdoOrderDate(dto.getCdoOrderDate())
.cdoOrderType(dto.getCdoOrderType())
.cdoRequestNote(dto.getCdoRequestNote())
.cdoDriverId(driverId)
.cdoCustomerId(customerId)
.cdoCustomerNo(dto.getCdoCustomerNo())
.cdoPaymentType(dto.getCdoPaymentType())
.cdoCycle(dto.getCdoCycle())
.cdoRate(dto.getCdoRate())
.cdoCreatedBy(dto.getCdoLoginUser())
.cdoStatus(dto.getCdoStatus())
.cdoVisitFlag(dto.getCdoVisitFlag())
.cdoEstimatedQty(dto.getCdoEstimatedQty())
.cdoQuantity(dto.getCdoQuantity())
.cdoSludge(dto.getCdoSludge())
.cdoPayStatus(dto.getCdoPayStatus())
.cdoPayAmount(dto.getCdoPayAmount())
.cdoPayeeName(dto.getCdoPayeeName())
.cdoPayeeSign(dto.getCdoPayeeSign())
.cdoPickupAt(dto.getCdoPickupAt())
.cdoPickupNote(dto.getCdoPickupNote())
.cdoPickupLat(dto.getCdoPickupLat())
.cdoPickupLon(dto.getCdoPickupLon())
.cdoPickupMin(dto.getCdoPickupMin())
.build();
order = dailyOrderRepository.save(order);
return mapToDto(order);
}
/**
* GET ALL (with paging)
*/
public Page<CustomerDailyOrderResponseDto> getAllDailyOrders(int page, int size) {
Pageable pageable = PageRequest.of(page, size);
Page<CustomerDailyOrder> orders = dailyOrderRepository.findAll(pageable);
return orders.map(this::mapToDto);
}
/**
* GET BY UUID
*/
public CustomerDailyOrderResponseDto getDailyOrderByUuid(UUID uuid) {
CustomerDailyOrder order = dailyOrderRepository.findByCdoUuid(uuid)
.orElseThrow(() -> new RuntimeException("Daily Order not found"));
return mapToDto(order);
}
/**
* GET BY CUSTOMER + DATE
*/
public CustomerDailyOrderResponseDto getDailyOrderByCustomerNo(String cusNo, LocalDate orderDate) {
CustomerDailyOrder order = dailyOrderRepository
.findByCdoCustomerNoAndCdoOrderDate(cusNo, orderDate)
.orElseThrow(() -> new RuntimeException("Order not found"));
return mapToDto(order);
}
/**
* UPDATE BY CUSTOMER + DATE
*/
@Transactional
public CustomerDailyOrderResponseDto updateDailyOrderByCustomerNoAndOrderDate(
String customerNo,
LocalDate orderDate,
CustomerDailyOrderRequestDto dto
) {
// daily order 조회
CustomerDailyOrder existing = dailyOrderRepository
.findByCdoCustomerNoAndCdoOrderDate(customerNo, orderDate)
.orElseThrow(() -> new RuntimeException("Daily Order not found"));
// 기존 updateInternal 공통 업데이트 처리
updateInternal(existing, dto);
// 저장
dailyOrderRepository.save(existing);
return mapToDto(existing);
}
/**
* UPDATE
*/
@Transactional
public CustomerDailyOrderResponseDto updateDailyOrder(UUID uuid, CustomerDailyOrderRequestDto dto) {
CustomerDailyOrder existing = dailyOrderRepository.findByCdoUuid(uuid)
.orElseThrow(() -> new RuntimeException("Daily Order not found"));
updateInternal(existing, dto);
existing.setCdoUpdatedAt(LocalDateTime.now());
existing.setCdoUpdatedBy(dto.getCdoLoginUser());
dailyOrderRepository.save(existing);
return mapToDto(existing);
}
/**
* DELETE
*/
public void deleteDailyOrder(UUID uuid) {
CustomerDailyOrder existing = dailyOrderRepository.findByCdoUuid(uuid)
.orElseThrow(() -> new RuntimeException("Daily Order not found"));
dailyOrderRepository.delete(existing);
}
/**
* Internal Update Logic (CustomerService 동일 스타일)
*/
private void updateInternal(CustomerDailyOrder order, CustomerDailyOrderRequestDto dto) {
// driver id (employee id)
Long driverId = resolveDriverId(dto);
// null 아닌 경우만 업데이트
if (dto.getCdoOrderType() != null) order.setCdoOrderType(dto.getCdoOrderType());
if (dto.getCdoRequestNote() != null) order.setCdoRequestNote(dto.getCdoRequestNote());
if (dto.getCdoDriverId() != null || dto.getCdoExternalDriverId() != null || dto.getCdoDriverUuid() != null) order.setCdoDriverId(driverId);
if (dto.getCdoPaymentType() != null) order.setCdoPaymentType(dto.getCdoPaymentType());
if (dto.getCdoCycle() != null) order.setCdoCycle(dto.getCdoCycle());
if (dto.getCdoRate() != null) order.setCdoRate(dto.getCdoRate());
if (dto.getCdoStatus() != null) order.setCdoStatus(dto.getCdoStatus());
if (dto.getCdoVisitFlag() != null) order.setCdoVisitFlag(dto.getCdoVisitFlag());
if (dto.getCdoEstimatedQty()!= null) order.setCdoEstimatedQty(dto.getCdoEstimatedQty());
if (dto.getCdoQuantity() != null) order.setCdoQuantity(dto.getCdoQuantity());
if (dto.getCdoSludge() != null) order.setCdoSludge(dto.getCdoSludge());
if (dto.getCdoPayStatus() != null) order.setCdoPayStatus(dto.getCdoPayStatus());
if (dto.getCdoPayAmount() != null) order.setCdoPayAmount(dto.getCdoPayAmount());
if (dto.getCdoPayeeName() != null) order.setCdoPayeeName(dto.getCdoPayeeName());
if (dto.getCdoPayeeSign() != null) order.setCdoPayeeSign(dto.getCdoPayeeSign());
if (dto.getCdoPickupAt() != null) order.setCdoPickupAt(dto.getCdoPickupAt());
if (dto.getCdoPickupNote() != null) order.setCdoPickupNote(dto.getCdoPickupNote());
if (dto.getCdoPickupLat() != null) order.setCdoPickupLat(dto.getCdoPickupLat());
if (dto.getCdoPickupLon() != null) order.setCdoPickupLon(dto.getCdoPickupLon());
if (dto.getCdoPickupMin() != null) order.setCdoPickupMin(dto.getCdoPickupMin());
}
/**
* ENTITY RESPONSE DTO
*/
public CustomerDailyOrderResponseDto mapToDto(CustomerDailyOrder order) {
if (order == null) return null;
return CustomerDailyOrderResponseDto.builder()
.cdoUuid(order.getCdoUuid())
.cdoOrderDate(order.getCdoOrderDate())
.cdoOrderType(order.getCdoOrderType())
.cdoRequestNote(order.getCdoRequestNote())
// .cdoDriverId(order.getCdoDriverId())
.cdoCustomerNo(order.getCdoCustomerNo())
.cdoPaymentType(order.getCdoPaymentType())
.cdoCycle(order.getCdoCycle())
.cdoRate(order.getCdoRate())
.cdoCreatedAt(order.getCdoCreatedAt())
.cdoCreatedBy(order.getCdoCreatedBy())
.cdoUpdatedAt(order.getCdoUpdatedAt())
.cdoUpdatedBy(order.getCdoUpdatedBy())
.cdoStatus(order.getCdoStatus())
.cdoVisitFlag(order.getCdoVisitFlag())
.cdoEstimatedQty(order.getCdoEstimatedQty())
.cdoQuantity(order.getCdoQuantity())
.cdoSludge(order.getCdoSludge())
.cdoPayStatus(order.getCdoPayStatus())
.cdoPayAmount(order.getCdoPayAmount())
.cdoPayeeName(order.getCdoPayeeName())
.cdoPayeeSign(order.getCdoPayeeSign())
.cdoPickupAt(order.getCdoPickupAt())
.cdoPickupNote(order.getCdoPickupNote())
.cdoPickupLat(order.getCdoPickupLat())
.cdoPickupLon(order.getCdoPickupLon())
.cdoPickupMin(order.getCdoPickupMin())
.build();
}
private Long resolveCustomerId(CustomerDailyOrderRequestDto dto) {
// 1. ERP customerId 직접 전달
if (dto.getCdoCustomerId() != null) {
return dto.getCdoCustomerId();
}
// 2. ERP customerUuid 전달
if (dto.getCdoCustomerUuid() != null) {
return customerRepository.findByCusUuid(dto.getCdoCustomerUuid())
.map(c -> c.getCusId())
.orElseThrow(() -> new RuntimeException("Customer not found by UUID"));
}
// 3. MIS customerNo만 있을
if (dto.getCdoCustomerNo() != null) {
return customerRepository.findByCusNo(dto.getCdoCustomerNo())
.map(c -> c.getCusId())
.orElseThrow(() -> new RuntimeException("Customer not found by customerNo"));
}
return null; // 또는 throw
}
private Long resolveDriverId(CustomerDailyOrderRequestDto dto) {
// 1. driver id
if (dto.getCdoDriverId() != null) {
return dto.getCdoDriverId();
}
// 2. MIS -> externalId (문자열 email or id)
if (dto.getCdoExternalDriverId() != null) {
return hcmEmployeeClient.getEmpIdFromExternalId( dto.getCdoExternalDriverId() );
}
// 3. ERP 내부 요청 (driverUuid 방식)
if (dto.getCdoDriverUuid() != null) {
return hcmEmployeeClient.getEmpIdFromUuid(dto.getCdoDriverUuid());
}
return null;
}
}

View File

@ -1,339 +0,0 @@
package com.goi.erp.service;
import com.goi.erp.dto.CustomerRequestDto;
import com.goi.erp.dto.CustomerResponseDto;
import com.goi.erp.entity.Customer;
import com.goi.erp.entity.EntityChangeLog;
import com.goi.erp.repository.CustomerRepository;
import com.goi.erp.repository.EntityChangeLogRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.UUID;
@Service
@RequiredArgsConstructor
public class CustomerService {
private final CustomerRepository customerRepository;
private final EntityChangeLogRepository entityChangeLogRepository;
public CustomerResponseDto createCustomer(CustomerRequestDto dto) {
Customer customer = Customer.builder()
.cusUuid(UUID.randomUUID())
.cusNo(dto.getCusNo())
.cusName(dto.getCusName())
.cusStatus(dto.getCusStatus())
.cusAreaId(dto.getCusAreaId())
.cusAddress1(dto.getCusAddress1())
.cusAddress2(dto.getCusAddress2())
.cusPostalCode(dto.getCusPostalCode())
.cusCity(dto.getCusCity())
.cusProvince(dto.getCusProvince())
.cusGeoLat(dto.getCusGeoLat() != null ? BigDecimal.valueOf(dto.getCusGeoLat()) : null)
.cusGeoLon(dto.getCusGeoLon() != null ? BigDecimal.valueOf(dto.getCusGeoLon()) : null)
.cusEmail(dto.getCusEmail())
.cusPhone(dto.getCusPhone())
.cusPhoneExt(dto.getCusPhoneExt())
.cusContractDate(dto.getCusContractDate())
.cusContractedBy(dto.getCusContractedBy())
.cusInstallDate(dto.getCusInstallDate())
.cusFullCycle(dto.getCusFullCycle())
.cusFullCycleFlag(dto.getCusFullCycleFlag())
.cusFullCycleForced(dto.getCusFullCycleForced())
.cusFullCycleForcedDate(dto.getCusFullCycleForcedDate())
.cusSchedule(dto.getCusSchedule())
.cusScheduledays(dto.getCusScheduledays())
.cusLastPaidDate(dto.getCusLastPaidDate())
.cusRate(dto.getCusRate() != null ? BigDecimal.valueOf(dto.getCusRate()) : null)
.cusPayMethod(dto.getCusPayMethod())
.cusAccountNo(dto.getCusAccountNo())
.cusIsccDate(dto.getCusIsccDate())
.cusCorsiaDate(dto.getCusCorsiaDate())
.cusHstNo(dto.getCusHstNo())
.cusOpenTime(dto.getCusOpenTime())
.cusComment(dto.getCusComment())
.cusContactComment(dto.getCusContactComment())
.cusInstallLocation(dto.getCusInstallLocation())
.build();
customer = customerRepository.save(customer);
return mapToDto(customer); // 생성 시에는 여전히 엔티티 DTO 필요
}
public Page<CustomerResponseDto> getAllCustomers(int page, int size) {
Pageable pageable = PageRequest.of(page, size);
Page<Customer> customers = customerRepository.findAll(pageable);
return customers.map(this::mapToDto); // 기존 mapToDto 사용
}
public CustomerResponseDto getCustomerByUuid(UUID uuid) {
Customer customer = customerRepository.findByCusUuid(uuid)
.orElseThrow(() -> new RuntimeException("Customer not found"));
return mapToDto(customer);
}
public CustomerResponseDto updateCustomer(UUID uuid, CustomerRequestDto dto) {
Customer customer = customerRepository.findByCusUuid(uuid)
.orElseThrow(() -> new RuntimeException("Customer not found"));
return updateCustomerInternal(customer, dto);
}
public void deleteCustomer(UUID uuid) {
Customer customer = customerRepository.findByCusUuid(uuid)
.orElseThrow(() -> new RuntimeException("Customer not found"));
customerRepository.delete(customer);
}
private CustomerResponseDto updateCustomerInternal(Customer customer, CustomerRequestDto dto) {
if (dto.getCusName() != null) customer.setCusName(dto.getCusName());
if (dto.getCusStatus() != null) customer.setCusStatus(dto.getCusStatus());
if (dto.getCusNo() != null) customer.setCusNo(dto.getCusNo());
if (dto.getCusAreaId() != null) customer.setCusAreaId(dto.getCusAreaId());
if (dto.getCusAddress1() != null) customer.setCusAddress1(dto.getCusAddress1());
if (dto.getCusAddress2() != null) customer.setCusAddress2(dto.getCusAddress2());
if (dto.getCusPostalCode() != null) customer.setCusPostalCode(dto.getCusPostalCode());
if (dto.getCusCity() != null) customer.setCusCity(dto.getCusCity());
if (dto.getCusProvince() != null) customer.setCusProvince(dto.getCusProvince());
if (dto.getCusGeoLat() != null) customer.setCusGeoLat(BigDecimal.valueOf(dto.getCusGeoLat()));
if (dto.getCusGeoLon() != null) customer.setCusGeoLon(BigDecimal.valueOf(dto.getCusGeoLon()));
if (dto.getCusEmail() != null) customer.setCusEmail(dto.getCusEmail());
if (dto.getCusPhone() != null) customer.setCusPhone(dto.getCusPhone());
if (dto.getCusPhoneExt() != null) customer.setCusPhoneExt(dto.getCusPhoneExt());
if (dto.getCusContractDate() != null) customer.setCusContractDate(dto.getCusContractDate());
if (dto.getCusContractedBy() != null) customer.setCusContractedBy(dto.getCusContractedBy());
if (dto.getCusInstallDate() != null) customer.setCusInstallDate(dto.getCusInstallDate());
if (dto.getCusFullCycle() != null) customer.setCusFullCycle(dto.getCusFullCycle());
if (dto.getCusFullCycleFlag() != null) customer.setCusFullCycleFlag(dto.getCusFullCycleFlag());
if (dto.getCusFullCycleForced() != null) customer.setCusFullCycleForced(dto.getCusFullCycleForced());
if (dto.getCusFullCycleForcedDate() != null) customer.setCusFullCycleForcedDate(dto.getCusFullCycleForcedDate());
if (dto.getCusSchedule() != null) customer.setCusSchedule(dto.getCusSchedule());
if (dto.getCusScheduledays() != null) customer.setCusScheduledays(dto.getCusScheduledays());
if (dto.getCusLastPaidDate() != null) customer.setCusLastPaidDate(dto.getCusLastPaidDate());
if (dto.getCusRate() != null) customer.setCusRate(BigDecimal.valueOf(dto.getCusRate()));
if (dto.getCusPayMethod() != null) customer.setCusPayMethod(dto.getCusPayMethod());
if (dto.getCusIsccDate() != null) customer.setCusIsccDate(dto.getCusIsccDate());
if (dto.getCusCorsiaDate() != null) customer.setCusCorsiaDate(dto.getCusCorsiaDate());
if (dto.getCusHstNo() != null) customer.setCusHstNo(dto.getCusHstNo());
if (dto.getCusTerminatedDate() != null) customer.setCusTerminatedDate(dto.getCusTerminatedDate());
if (dto.getCusTerminationReason() != null) customer.setCusTerminationReason(dto.getCusTerminationReason());
if (dto.getCusLastPickupDate() != null) customer.setCusLastPickupDate(dto.getCusLastPickupDate());
if (dto.getCusLastPickupQty() != null) customer.setCusLastPickupQty(dto.getCusLastPickupQty());
if (dto.getCusLastPickupLat() != null) customer.setCusLastPickupLat(BigDecimal.valueOf(dto.getCusLastPickupLat()));
if (dto.getCusLastPickupLon() != null) customer.setCusLastPickupLon(BigDecimal.valueOf(dto.getCusLastPickupLon()));
if (dto.getCusOpenTime() != null) customer.setCusOpenTime(dto.getCusOpenTime());
if (dto.getCusComment() != null) customer.setCusComment(dto.getCusComment());
if (dto.getCusContactComment() != null) customer.setCusContactComment(dto.getCusContactComment());
if (dto.getCusInstallLocation() != null) customer.setCusInstallLocation(dto.getCusInstallLocation());
customerRepository.save(customer);
return mapToDto(customer);
}
public CustomerResponseDto mapToDto(Customer customer) {
if (customer == null) return null;
CustomerResponseDto dto = new CustomerResponseDto();
dto.setCusUuid(customer.getCusUuid());
dto.setCusNo(customer.getCusNo());
dto.setCusName(customer.getCusName());
dto.setCusStatus(customer.getCusStatus());
dto.setCusAddress1(customer.getCusAddress1());
dto.setCusAddress2(customer.getCusAddress2());
dto.setCusCity(customer.getCusCity());
dto.setCusProvince(customer.getCusProvince());
dto.setCusGeoLat(customer.getCusGeoLat());
dto.setCusGeoLon(customer.getCusGeoLon());
dto.setCusEmail(customer.getCusEmail());
dto.setCusPhone(customer.getCusPhone());
dto.setCusPhoneExt(customer.getCusPhoneExt());
dto.setCusContractDate(customer.getCusContractDate());
dto.setCusContractedBy(customer.getCusContractedBy());
dto.setCusInstallDate(customer.getCusInstallDate());
dto.setCusInstallLocation(customer.getCusInstallLocation());
dto.setCusLastPickupDate(customer.getCusLastPickupDate());
dto.setCusLastPickupQty(customer.getCusLastPickupQty());
dto.setCusLastPickupLat(customer.getCusLastPickupLat());
dto.setCusLastPickupLon(customer.getCusLastPickupLon());
dto.setCusFullCycle(customer.getCusFullCycle());
dto.setCusFullCycleFlag(customer.getCusFullCycleFlag());
dto.setCusFullCycleForced(customer.getCusFullCycleForced());
dto.setCusFullCycleForcedDate(customer.getCusFullCycleForcedDate());
dto.setCusSchedule(customer.getCusSchedule());
dto.setCusScheduledays(customer.getCusScheduledays());
dto.setCusLastPaidDate(customer.getCusLastPaidDate());
dto.setCusRate(customer.getCusRate());
dto.setCusPayMethod(customer.getCusPayMethod());
dto.setCusAccountNo(customer.getCusAccountNo());
dto.setCusIsccDate(customer.getCusIsccDate());
dto.setCusCorsiaDate(customer.getCusCorsiaDate());
dto.setCusHstNo(customer.getCusHstNo());
dto.setCusTerminatedDate(customer.getCusTerminatedDate());
dto.setCusTerminationReason(customer.getCusTerminationReason());
dto.setCusLastPickupMin(customer.getCusLastPickupMin());
dto.setCusOpenTime(customer.getCusOpenTime());
dto.setCusComment(customer.getCusComment());
dto.setCusContactComment(customer.getCusContactComment());
dto.setCusInstallLocation(customer.getCusInstallLocation());
return dto;
}
// from MIS
public CustomerResponseDto getCustomerByNo(String cusNo) {
Customer customer = customerRepository.findByCusNo(cusNo)
.orElseThrow(() -> new RuntimeException("Customer not found"));
return mapToDto(customer);
}
@Transactional
public CustomerResponseDto updateCustomerByNo(String cusNo, CustomerRequestDto newCustomer) {
Customer oldCustomer = customerRepository.findByCusNo(cusNo)
.orElseThrow(() -> new RuntimeException("Customer not found"));
// 1. OLD VALUE 백업 (deep copy)
Customer beforeUpdate = new Customer();
BeanUtils.copyProperties(oldCustomer, beforeUpdate);
// 2. 주소/우편번호 변경 geo 초기화
if ((newCustomer.getCusAddress1() != null && !newCustomer.getCusAddress1().equals(oldCustomer.getCusAddress1())) ||
(newCustomer.getCusPostalCode() != null && !newCustomer.getCusPostalCode().equals(oldCustomer.getCusPostalCode()))) {
oldCustomer.setCusGeoLat(null);
oldCustomer.setCusGeoLon(null);
}
// 3. 실제 업데이트 적용
CustomerResponseDto response = updateCustomerInternal(oldCustomer, newCustomer);
// 4. 변경 비교 (old vs new)
String misLoginUser = newCustomer.getCusLoginUser();
compareAndLogChanges(beforeUpdate, oldCustomer, misLoginUser);
return response;
}
// set change log
private void compareAndLogChanges(Customer oldData, Customer newData, String changedBy) {
// 필드 DB 컬럼 매핑
Map<String, String> fieldToColumn = Map.ofEntries(
Map.entry("cusName", "cus_name"),
Map.entry("cusEmail", "cus_email"),
Map.entry("cusPhone", "cus_phone"),
Map.entry("cusGeoLat", "cus_geo_lat"),
Map.entry("cusGeoLon", "cus_geo_lon"),
Map.entry("cusStatus", "cus_status"),
Map.entry("cusAddress1", "cus_address1"),
Map.entry("cusAddress2", "cus_address2"),
Map.entry("cusPostalCode", "cus_postal_code"),
Map.entry("cusCity", "cus_city"),
Map.entry("cusProvince", "cus_province"),
Map.entry("cusPhoneExt", "cus_phone_ext"),
Map.entry("cusContractDate", "cus_contract_date"),
Map.entry("cusContractedBy", "cus_contracted_by"),
Map.entry("cusInstallDate", "cus_install_date"),
Map.entry("cusFullCycle", "cus_full_cycle"),
Map.entry("cusFullCycleFlag", "cus_full_cycle_flag"),
Map.entry("cusFullCycleForced", "cus_full_cycle_forced"),
Map.entry("cusFullCycleForcedDate", "cus_full_cycle_forced_date"),
Map.entry("cusSchedule", "cus_schedule"),
Map.entry("cusScheduledays", "cus_scheduledays"),
Map.entry("cusLastPaidDate", "cus_last_paid_date"),
Map.entry("cusRate", "cus_rate"),
Map.entry("cusPayMethod", "cus_pay_method"),
Map.entry("cusAccountNo", "cus_account_no"),
Map.entry("cusIsccDate", "cus_iscc_date"),
Map.entry("cusCorsiaDate", "cus_corsia_date"),
Map.entry("cusHstNo", "cus_hst_no"),
Map.entry("cusOpenTime", "cus_open_time"),
Map.entry("cusComment", "cus_comment"),
Map.entry("cusContactComment", "cus_contact_comment"),
Map.entry("cusInstallLocation", "cus_install_location")
);
Class<?> clazz = Customer.class;
for (var entry : fieldToColumn.entrySet()) {
String fieldName = entry.getKey();
String columnName = entry.getValue();
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
Object oldVal = field.get(oldData);
Object newVal = field.get(newData);
if (valuesAreDifferent(oldVal, newVal)) {
entityChangeLogRepository.save(
EntityChangeLog.builder()
.eclEntityType("Customer")
.eclEntityId(newData.getCusId())
.eclFieldName(fieldName)
.eclColumnName(columnName)
.eclOldValue(oldVal == null ? null : oldVal.toString())
.eclNewValue(newVal == null ? null : newVal.toString())
.eclEffectiveDate(LocalDate.now())
.eclChangedBy(changedBy)
.eclChangedAt(LocalDateTime.now())
.build()
);
}
} catch (Exception e) {
throw new RuntimeException("Failed to compare field: " + fieldName, e);
}
}
}
private boolean valuesAreDifferent(Object oldVal, Object newVal) {
// null 변경 없음
if (oldVal == null && newVal == null) return false;
// 한쪽만 null 변경됨
if (oldVal == null || newVal == null) return true;
// BigDecimal (numeric 비교)
if (oldVal instanceof BigDecimal oldNum && newVal instanceof BigDecimal newNum) {
return oldNum.compareTo(newNum) != 0; // scale 무시 비교
}
// LocalDate
if (oldVal instanceof LocalDate oldDate && newVal instanceof LocalDate newDate) {
return !oldDate.isEqual(newDate);
}
// LocalDateTime
if (oldVal instanceof LocalDateTime oldDt && newVal instanceof LocalDateTime newDt) {
return !oldDt.equals(newDt);
}
// Boolean
if (oldVal instanceof Boolean && newVal instanceof Boolean) {
return !oldVal.equals(newVal);
}
// (String 포함) 기본 equals 비교
return !oldVal.equals(newVal);
}
}