Compare commits

...
13 Commits
54 changed files with 1503 additions and 280 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,25 @@
package net.jwsi.jcms.vpp.RatePlanDetailTime;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.jwsi.jcms.base.BaseStEntity;
@NoArgsConstructor
@Setter
@Getter
@Entity(name="vpp_rate_plan_detail_time")
public class RatePlanDetailTime extends BaseStEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer detailTimeId; //상세 id (계시별 단가 기준)(pk)
private Integer ratePlanId; //요금제 id (fk)
private Integer detailCapacityId; //상세 id (충전용량 기준, fk)
private Integer timeSlotId; //시간대 id (fk: time_slots)(fk)
private Double price; //시간대별 단가
private String seasonType; //계절 타입
}
@@ -0,0 +1,13 @@
package net.jwsi.jcms.vpp.RatePlanDetailTime;
import net.jwsi.jcms.base.BaseRecStRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface RatePlanDetailTimeRepository extends BaseRecStRepository<RatePlanDetailTime, Integer> {
void deleteByRatePlanId(Integer ratePlanId);
List<RatePlanDetailTime> findByRatePlanId(Integer ratePlanId);
}
@@ -5,13 +5,12 @@ import net.jwsi.jcms.vpp.chgr.ChgrResponseDto;
import net.jwsi.jcms.vpp.chgrCurrState.ChgrCurrState; import net.jwsi.jcms.vpp.chgrCurrState.ChgrCurrState;
import net.jwsi.jcms.vpp.chgrHist.ChgrHistResponseDto; import net.jwsi.jcms.vpp.chgrHist.ChgrHistResponseDto;
import net.jwsi.jcms.vpp.provider.ProviderDto; import net.jwsi.jcms.vpp.provider.ProviderDto;
import net.jwsi.jcms.vpp.rehchgingList.RechgingList; import net.jwsi.jcms.vpp.rechgingList.RechgingListDto;
import net.jwsi.jcms.vpp.rehchgingList.RechgingListDto; import net.jwsi.jcms.vpp.ratePlan.RatePlanResponseDto;
import net.jwsi.jcms.vpp.station.StationDto; import net.jwsi.jcms.vpp.station.StationDto;
import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.ParameterizedTypeReference;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClient;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -58,11 +57,14 @@ public class ApiClient {
.body(new ParameterizedTypeReference<ApiResponse<List<ChgrHistResponseDto>>>() {}); .body(new ParameterizedTypeReference<ApiResponse<List<ChgrHistResponseDto>>>() {});
} }
public ApiResponse<List<ChgrCurrState>> fetchChgrCurrState() { public ApiResponse<List<ChgrCurrState>> fetchChgrCurrState(String pageSize) {
Map<String, String> requestBody = Map.of(
"pageSize", pageSize
);
return externalApiClient.post() return externalApiClient.post()
.uri("/api/charge/stateInfo") .uri("/api/charge/stateInfo")
.body(Collections.emptyMap()) .body(requestBody)
.retrieve() .retrieve()
.body(new ParameterizedTypeReference<ApiResponse<List<ChgrCurrState>>>() {}); .body(new ParameterizedTypeReference<ApiResponse<List<ChgrCurrState>>>() {});
} }
@@ -77,4 +79,23 @@ public class ApiClient {
} }
public ApiResponse<List<RatePlanResponseDto>> fetchRatePlan(){
Map<String, String> requestBody = Map.of(
"type","all",
"pageSize", "10000"
);
return externalApiClient.post()
.uri("/api/ratePlan/list")
.body(requestBody)
.retrieve()
.body(new ParameterizedTypeReference<ApiResponse<List<RatePlanResponseDto>>>() {
});
}
public ApiResponse<?> registerRatePlan(RatePlanResponseDto requestDto) {
return externalApiClient.post()
.uri("/api/ratePlan/register")
.body(requestDto)
.retrieve()
.body(ApiResponse.class);
}
} }
@@ -0,0 +1,26 @@
package net.jwsi.jcms.vpp.chargerRatePlan;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.jwsi.jcms.base.BaseStEntity;
@NoArgsConstructor
@Setter
@Getter
@Entity(name="vpp_charger_rate_plan")
public class ChargerRatePlan extends BaseStEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // 🚨 무조건 추가!
private Integer id; //적용 이력 id(pk)
private String chargerCd; //충전기 코드 (사업자코드+충전소번호+충전기번호)
private Integer ratePlanId; //적용 요금제 id (fk)
private String applyStartDate; //적용 시작일
private String applyEndDate; //적용 종료일 (null = 현재 적용중)
private String isActive; //활성화 여부
private String notes; //비고 / 변경 이유
}
@@ -0,0 +1,12 @@
package net.jwsi.jcms.vpp.chargerRatePlan;
import net.jwsi.jcms.base.BaseRecStRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ChargerRatePlanRepository extends BaseRecStRepository<ChargerRatePlan,Integer> {
void deleteByRatePlanId(Integer ratePlanId);
List<ChargerRatePlan> findByRatePlanId(Integer ratePlanId);
}
@@ -1,8 +1,6 @@
package net.jwsi.jcms.vpp.chgr; package net.jwsi.jcms.vpp.chgr;
import jakarta.persistence.Column; import jakarta.persistence.*;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
@@ -14,12 +12,20 @@ import net.jwsi.jcms.base.BaseStEntity;
@Getter @Getter
@ToString @ToString
@Entity(name = "vpp_chgr") @Entity(name = "vpp_chgr")
@IdClass(ChgrId.class)
public class Chgr extends BaseStEntity { public class Chgr extends BaseStEntity {
@Transient
private ChgrId oldId__;
@Id @Id
@Column(name = "CHGR_ID") @Column(name = "CHGR_ID")
private String chgrId; //충전기 id(pk) private String chgrId; //충전기 id(pk)
@Id
@Column(name = "PROVIDER_ID") @Column(name = "PROVIDER_ID")
private String providerId; //충전사업자 id(pk) @Id private String providerId; //충전사업자 id(pk) @Id
@Id
@Column(name = "ST_ID")
private String stId; //충전소 id(pk) @Id private String stId; //충전소 id(pk) @Id
private String stNm; //충전소 명 private String stNm; //충전소 명
private String chgrNm; //충전기 명 private String chgrNm; //충전기 명
@@ -14,7 +14,7 @@ import java.util.Map;
@Controller @Controller
@Slf4j @Slf4j
@RequestMapping("/cms/chgr") @RequestMapping("/cms/chgr/")
public class ChgrController extends BaseController<Chgr,String,ChgrRepository,ChgrService> { public class ChgrController extends BaseController<Chgr,String,ChgrRepository,ChgrService> {
private final String path = "/system/chgr/"; private final String path = "/system/chgr/";
@@ -28,6 +28,7 @@ public class ChgrController extends BaseController<Chgr,String,ChgrRepository,Ch
} }
@GetMapping("list") @GetMapping("list")
public String list(){ public String list(){
chgrService.getCharger();
return path+"list"; return path+"list";
} }
@PostMapping("list.json") @PostMapping("list.json")
@@ -0,0 +1,18 @@
package net.jwsi.jcms.vpp.chgr;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class ChgrId implements Serializable {
private String providerId;
private String stId;
private String chgrId;
}
@@ -12,7 +12,7 @@ public class ChgrResponseDto {
private String chgrId; private String chgrId;
private String chgrNm; private String chgrNm;
private String speedTp; private String speedTp;
private String chgrTp; private String chgrType;
private double gpsXpos; private double gpsXpos;
private double gpsYpos; private double gpsYpos;
private String locInfo; private String locInfo;
@@ -29,7 +29,7 @@ public class ChgrResponseDto {
chgr.setStNm(this.stNm); chgr.setStNm(this.stNm);
chgr.setChgrNm(this.chgrNm); chgr.setChgrNm(this.chgrNm);
chgr.setSpeedTp(this.speedTp); chgr.setSpeedTp(this.speedTp);
chgr.setChgrTp(this.chgrTp); chgr.setChgrTp(this.chgrType);
return chgr; return chgr;
} }
} }
@@ -73,7 +73,12 @@ public class ChgrService extends BaseService<Chgr,String,ChgrRepository> {
@Override @Override
protected Specification<Chgr> getSpecification(Chgr chgr) { protected Specification<Chgr> getSpecification(Chgr chgr) {
return null; Specification<Chgr> rst = null;
if(chgr == null) return null;
rst = addWhere(rst, chgr.getChgrId(), ChgrSpecification.getChgrId(chgr.getChgrId()));
rst = addWhere(rst, chgr.getStId(), ChgrSpecification.getStId(chgr.getStId()));
return rst;
} }
@Override @Override
@@ -1,4 +1,17 @@
package net.jwsi.jcms.vpp.chgr; package net.jwsi.jcms.vpp.chgr;
public class ChgrSpecification { import net.jwsi.jcms.base.BaseSpecification;
import org.springframework.data.jpa.domain.Specification;
public class ChgrSpecification extends BaseSpecification {
public static Specification<Chgr> getStId(String stId) {
return (root, query, cb) -> {
return cb.equal(root.get("stId"), stId);
};
}
public static Specification<Chgr> getChgrId(String chgrId) {
return (root, query, cb) -> {
return cb.equal(root.get("chgrId"), chgrId);
};
}
} }
@@ -12,7 +12,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
@Controller @Controller
@RequestMapping("/cms/chgrCurrState") @RequestMapping("/cms/chgrCurrState/")
public class ChgrCurrStateController extends BaseController<ChgrCurrState,ChgrCurrStateId,ChgrCurrStateRepository,ChgrCurrStateService> { public class ChgrCurrStateController extends BaseController<ChgrCurrState,ChgrCurrStateId,ChgrCurrStateRepository,ChgrCurrStateService> {
private final ChgrCurrStateService chgrCurrStateService; private final ChgrCurrStateService chgrCurrStateService;
@@ -29,7 +29,7 @@ public class ChgrCurrStateService extends BaseService<ChgrCurrState,ChgrCurrStat
@Transactional @Transactional
@Scheduled(cron = "0 0 0 * * *") @Scheduled(cron = "0 0 0 * * *")
public void getChgrCurrState() { public void getChgrCurrState() {
ApiResponse<List<ChgrCurrState>> response = apiClient.fetchChgrCurrState(); ApiResponse<List<ChgrCurrState>> response = apiClient.fetchChgrCurrState("12");
if (response == null || !Integer.valueOf(200).equals(response.getCode())) { if (response == null || !Integer.valueOf(200).equals(response.getCode())) {
String msg = (response != null) ? response.getMessage() : "응답 없음"; String msg = (response != null) ? response.getMessage() : "응답 없음";
@@ -72,7 +72,15 @@ public class ChgrCurrStateService extends BaseService<ChgrCurrState,ChgrCurrStat
@Override @Override
protected Specification<ChgrCurrState> getSpecification(ChgrCurrState chgrCurrState) { protected Specification<ChgrCurrState> getSpecification(ChgrCurrState chgrCurrState) {
return null; Specification<ChgrCurrState> rst = null;
if(chgrCurrState == null) return null;
rst = addWhere(rst, chgrCurrState.getChgrId(), ChgrCurrStateSpecification.getChgrId(chgrCurrState.getChgrId()));
rst = addWhere(rst, chgrCurrState.getStId(), ChgrCurrStateSpecification.getStId(chgrCurrState.getStId()));
rst = addWhere(rst, chgrCurrState.getCh1DoorStateCd(), ChgrCurrStateSpecification.getDoorState(chgrCurrState.getCh1DoorStateCd()));
rst = addWhere(rst, chgrCurrState.getCh1PlugStateCd(), ChgrCurrStateSpecification.getPlugState(chgrCurrState.getCh1PlugStateCd()));
rst = addWhere(rst, chgrCurrState.getCh1RechgStateCd(), ChgrCurrStateSpecification.getRechgState(chgrCurrState.getCh1RechgStateCd()));
return rst;
} }
@Override @Override
@@ -0,0 +1,35 @@
package net.jwsi.jcms.vpp.chgrCurrState;
import net.jwsi.jcms.base.BaseSpecification;
import org.springframework.data.jpa.domain.Specification;
public class ChgrCurrStateSpecification extends BaseSpecification {
public static Specification<ChgrCurrState> getStId(String stId) {
return (root, query, cb) -> {
return cb.equal(root.get("stId"), stId);
};
}
public static Specification<ChgrCurrState> getChgrId(String chgrId) {
return (root, query, cb) -> {
return cb.equal(root.get("chgrId"), chgrId);
};
}
public static Specification<ChgrCurrState> getDoorState(String doorState) {
return (root, query, cb) -> {
return cb.equal(root.get("ch1DoorStateCd"), doorState);
};
}
public static Specification<ChgrCurrState> getPlugState(String plugState) {
return (root, query, cb) -> {
return cb.equal(root.get("ch1PlugStateCd"), plugState);
};
}
public static Specification<ChgrCurrState> getRechgState(String rechgState) {
return (root, query, cb) -> {
return cb.equal(root.get("ch1RechgStateCd"), rechgState);
};
}
}
@@ -22,6 +22,9 @@ public class ChgrHist extends BaseStEntity {
@Column(name = "CHGR_ID", nullable = false) @Column(name = "CHGR_ID", nullable = false)
private String chgrId; // 충전기 id private String chgrId; // 충전기 id
@Column(name = "CH_ID")
private Integer chId;
@Column(name = "ST_ID", nullable = false) @Column(name = "ST_ID", nullable = false)
private String stId; // 충전소 id private String stId; // 충전소 id
@@ -10,7 +10,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
@Controller @Controller
@RequestMapping("/cms/chgrHist") @RequestMapping("/cms/chgrHist/")
@Slf4j @Slf4j
public class ChgrHistController extends BaseController<ChgrHist,Integer,ChgrHistRepository,ChgrHistService> { public class ChgrHistController extends BaseController<ChgrHist,Integer,ChgrHistRepository,ChgrHistService> {
@@ -23,6 +23,7 @@ public class ChgrHistController extends BaseController<ChgrHist,Integer,ChgrHist
@GetMapping("list") @GetMapping("list")
public String list() throws IllegalAccessException { public String list() throws IllegalAccessException {
chgrHistService.getChgrHist();
return path+"list"; return path+"list";
} }
@@ -5,4 +5,5 @@ import org.springframework.stereotype.Repository;
@Repository @Repository
public interface ChgrHistRepository extends BaseRecStRepository<ChgrHist,Integer> { public interface ChgrHistRepository extends BaseRecStRepository<ChgrHist,Integer> {
boolean existsByChgrIdAndChIdAndRechgEDt(String chgrId, Integer chId,String rechgEDt);
} }
@@ -52,6 +52,7 @@ public class ChgrHistResponseDto {
chgrHist.setRechgAmt(this.rechgAmt); chgrHist.setRechgAmt(this.rechgAmt);
chgrHist.setPayFnsh(this.payFnsh); chgrHist.setPayFnsh(this.payFnsh);
chgrHist.setRechGwh(this.rechgWh); chgrHist.setRechGwh(this.rechgWh);
chgrHist.setChId(this.chId);
return chgrHist; return chgrHist;
} }
} }
@@ -30,7 +30,7 @@ public class ChgrHistService extends BaseService<ChgrHist, Integer, ChgrHistRepo
@Transactional @Transactional
@Scheduled(cron = "0 */10 * * * *") @Scheduled(cron = "0 */10 * * * *")
public void getChgrHist() throws IllegalAccessException { public void getChgrHist() {
String startDt = LocalDate.now().toString(); String startDt = LocalDate.now().toString();
ApiResponse<List<ChgrHistResponseDto>> response = apiClient.fetchChgrHist(startDt, "50"); ApiResponse<List<ChgrHistResponseDto>> response = apiClient.fetchChgrHist(startDt, "50");
@@ -51,21 +51,17 @@ public class ChgrHistService extends BaseService<ChgrHist, Integer, ChgrHistRepo
.toList(); .toList();
int insertCount = 0; int insertCount = 0;
int updateCount = 0;
for (ChgrHist h : entityList) { for (ChgrHist h : entityList) {
Integer id = BaseService.getId(h);
if (id != null && super.repository.existsById(id)) { boolean isExist = ((ChgrHistRepository) super.repository).existsByChgrIdAndChIdAndRechgEDt(h.getChgrId(), h.getChId(),h.getRechgEDt());
super.update(h);
updateCount++; if (!isExist) {
} else {
super.insert(h); super.insert(h);
insertCount++; insertCount++;
} }
} }
log.info("충전이력 동기화 완료 (신규 추가: {}건 / 기존 업데이트: {}건)", insertCount, updateCount); log.info("충전이력 수집 완료: API 응답 {}건 중 신규 저장 {}건", entityList.size(), insertCount);
} }
@Override @Override
@@ -93,7 +89,12 @@ public class ChgrHistService extends BaseService<ChgrHist, Integer, ChgrHistRepo
@Override @Override
protected Specification<ChgrHist> getSpecification(ChgrHist chgrHist) { protected Specification<ChgrHist> getSpecification(ChgrHist chgrHist) {
return null; Specification<ChgrHist> rst = null;
if(chgrHist == null) return null;
rst = addWhere(rst, chgrHist.getChgrId(), ChgrHistSpecification.getChgrId(chgrHist.getChgrId()));
rst = addWhere(rst, chgrHist.getStId(), ChgrHistSpecification.getStId(chgrHist.getStId()));
return rst;
} }
@Override @Override
@@ -0,0 +1,17 @@
package net.jwsi.jcms.vpp.chgrHist;
import net.jwsi.jcms.base.BaseSpecification;
import org.springframework.data.jpa.domain.Specification;
public class ChgrHistSpecification extends BaseSpecification {
public static Specification<ChgrHist> getStId(String stId) {
return (root, query, cb) -> {
return cb.equal(root.get("stId"), stId);
};
}
public static Specification<ChgrHist> getChgrId(String chgrId) {
return (root, query, cb) -> {
return cb.equal(root.get("chgrId"), chgrId);
};
}
}
@@ -26,6 +26,7 @@ public class ProviderController extends BaseController<Provider, String, Provide
@GetMapping("list") @GetMapping("list")
public String list() { public String list() {
providerService.getProviders();
return path + "list"; return path + "list";
} }
@@ -79,8 +79,13 @@ public class ProviderService extends BaseService<Provider, String, ProviderRepos
@Override @Override
protected Specification<Provider> getSpecification(Provider srch) { protected Specification<Provider> getSpecification(Provider srch) {
if (srch == null) return null; if (srch == null) return null;
Specification<Provider> rst = null; Specification<Provider> rst = null;
rst = addWhere(rst, srch.getProviderId(), ProviderSpecification.providerId(srch.getProviderId())); rst = addWhere(rst, srch.getProviderNm(), ProviderSpecification.providerNm(srch.getProviderNm()));
rst = addWhere(rst, srch.getBizTaxId(), ProviderSpecification.bizTaxId(srch.getBizTaxId()));
rst = addWhere(rst, srch.getCeo(), ProviderSpecification.ceo(srch.getCeo()));
rst = addWhere(rst, srch.getTel(), ProviderSpecification.tel(srch.getTel()));
rst = addWhere(rst, srch.getFax(), ProviderSpecification.fax(srch.getFax()));
return rst; return rst;
} }
@@ -1,10 +1,28 @@
package net.jwsi.jcms.vpp.provider; package net.jwsi.jcms.vpp.provider;
import net.jwsi.jcms.utils.StrUtil;
import net.jwsi.jcms.vpp.rechgingList.RechgingList;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
public class ProviderSpecification { public class ProviderSpecification {
public static Specification<Provider> providerId(String providerId) { public static Specification<Provider> providerNm(String providerNm) {
return (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("providerId"), providerId); return (root, query, cb) -> cb.like(root.get("providerNm"), StrUtil.sqlLikeParam(providerNm));
}
public static Specification<Provider> bizTaxId(String bizTaxId) {
return (root, query, cb) -> cb.like(root.get("bizTaxId"), StrUtil.sqlLikeParam(bizTaxId));
}
public static Specification<Provider> ceo(String ceo) {
return (root, query, cb) -> cb.like(root.get("ceo"), StrUtil.sqlLikeParam(ceo));
}
public static Specification<Provider> tel(String tel) {
return (root, query, cb) -> cb.like(root.get("tel"), StrUtil.sqlLikeParam(tel));
}
public static Specification<Provider> fax(String fax) {
return (root, query, cb) -> cb.like(root.get("fax"), StrUtil.sqlLikeParam(fax));
} }
} }
@@ -0,0 +1,29 @@
package net.jwsi.jcms.vpp.ratePlan;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.jwsi.jcms.base.BaseStEntity;
@NoArgsConstructor
@Setter
@Getter
@Entity(name="vpp_rate_plan")
public class RatePlan extends BaseStEntity {
@Id
private Integer ratePlanId; //요금제 id(pk)
private String ratePlanName; //요금제명
private String ratePlanType; //요금제 타입 (고정/계시별/충전용량별/계시+충전용량별)
private String seasonType; //계절 타입 (연중/봄/여름/가을/겨울)
private String applyStartDate; //적용 시작일
private String applyEndDate; //적용 종료일
private Double fixedPrice; //고정 단가 (rate_plan_type=fixed일 경우)
private String description; //요금제 설명
private String approvalStatus; //승인 상태
private String rejectionReason; //승인 거절 사유
@Column(name = "IS_ACTIVE")
private String isActive;
}
@@ -0,0 +1,90 @@
package net.jwsi.jcms.vpp.ratePlan;
import lombok.extern.slf4j.Slf4j;
import net.jwsi.jcms.base.BaseController;
import net.jwsi.jcms.exception.JsonDataException;
import net.jwsi.jcms.vpp.api.ApiClient;
import net.jwsi.jcms.vpp.api.ApiResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
@Slf4j
@RequestMapping("/cms/ratePlan/")
public class RatePlanController extends BaseController<RatePlan, Integer, RatePlanRepository, RatePlanService> {
private final String path = "/system/ratePlan/";
private final RatePlanService ratePlanService;
private final ApiClient apiClient;
public RatePlanController(RatePlanService service, RatePlanService ratePlanService, ApiClient apiClient) {
super(service);
this.ratePlanService = ratePlanService;
this.apiClient = apiClient;
}
@GetMapping("list")
public String list() {
ratePlanService.getRatePlan();
return path + "list";
}
@PostMapping("list.json")
@ResponseBody
public Map<String, Object> list(RatePlan ratePlan) {
if (ratePlan == null) ratePlan = new RatePlan();
return svcSelectList(ratePlan);
}
@PostMapping("view.json")
@ResponseBody
public Map<String, Object> viewData(@RequestParam("ratePlanId") Integer ratePlanId) {
Map<String, Object> result = new HashMap<>();
try {
RatePlanResponseDto detailData = ratePlanService.getRatePlanDetail(ratePlanId);
result.put("result", "success");
result.put("data", detailData);
} catch (Exception e) {
result.put("result", "fail");
result.put("message", e.getMessage());
}
return result;
}
@PostMapping("insert.json")
@ResponseBody
public Map<String, Object> insertRatePlan(@RequestBody RatePlanResponseDto requestDto) {
Map<String, Object> resultMap = new HashMap<>();
try {
ApiResponse<?> apiRes = apiClient.registerRatePlan(requestDto);
if (apiRes == null || !Integer.valueOf(200).equals(apiRes.getCode())) {
String msg = (apiRes != null) ? apiRes.getMessage() : "API 서버 응답 없음";
throw new RuntimeException("API 서버 등록 실패: " + msg);
}
resultMap.put("result", "success");
resultMap.put("message", "요금제 등록 요청이 완료되었습니다. (승인 대기)");
} catch (Exception e) {
log.error("요금제 등록 중 오류 발생", e);
resultMap.put("result", "fail");
resultMap.put("message", e.getMessage());
}
return resultMap;
}
@Override
protected void chkSelectList(List<RatePlan> list) throws JsonDataException {
}
@Override
protected void chkSelect(RatePlan data) throws JsonDataException {
}
}
@@ -0,0 +1,8 @@
package net.jwsi.jcms.vpp.ratePlan;
import net.jwsi.jcms.base.BaseRecStRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface RatePlanRepository extends BaseRecStRepository<RatePlan,Integer> {
}
@@ -0,0 +1,116 @@
package net.jwsi.jcms.vpp.ratePlan;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import net.jwsi.jcms.vpp.RatePlanDetailTime.RatePlanDetailTime;
import net.jwsi.jcms.vpp.chargerRatePlan.ChargerRatePlan;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@ToString
public class RatePlanResponseDto {
private Integer ratePlanId;
private String ratePlanName;
private String ratePlanType;
private String seasonType;
private String applyStartDate;
private String applyEndDate;
private Double fixedPrice;
private String isActive;
private String approvalStatus;
private String description;
// 계시별 단가 리스트
private List<DetailsTimeDto> detailsTime;
// 적용 대상 충전기 리스트
private List<ChargerListDto> chargerList;
public RatePlan toEntity() {
RatePlan ratePlan = new RatePlan();
ratePlan.setRatePlanId(this.ratePlanId);
ratePlan.setRatePlanName(this.ratePlanName);
ratePlan.setRatePlanType(this.ratePlanType);
ratePlan.setSeasonType(this.seasonType);
ratePlan.setApplyStartDate(this.applyStartDate);
ratePlan.setApplyEndDate(this.applyEndDate);
ratePlan.setFixedPrice(this.fixedPrice);
ratePlan.setIsActive(this.isActive);
ratePlan.setApprovalStatus(this.approvalStatus);
ratePlan.setDescription(this.description);
return ratePlan;
}
public List<RatePlanDetailTime> toDetailTimeEntities() {
List<RatePlanDetailTime> detailTimeList = new ArrayList<>();
if (this.detailsTime == null) return detailTimeList;
for (DetailsTimeDto seasonDto : this.detailsTime) {
if (seasonDto.getTimeRates() == null) continue;
for (TimeRateDto timeRateDto : seasonDto.getTimeRates()) {
RatePlanDetailTime detailTime = new RatePlanDetailTime();
detailTime.setRatePlanId(this.ratePlanId);
detailTime.setSeasonType(seasonDto.getSeasonType());
detailTime.setTimeSlotId(timeRateDto.getId());
detailTime.setPrice(timeRateDto.getPrice());
detailTimeList.add(detailTime);
}
}
return detailTimeList;
}
public List<ChargerRatePlan> toChargerRatePlanEntities() {
List<ChargerRatePlan> chargerRatePlanList = new ArrayList<>();
if (this.chargerList == null) return chargerRatePlanList;
for (ChargerListDto chargerDto : this.chargerList) {
ChargerRatePlan chargerRatePlan = new ChargerRatePlan();
if (this.ratePlanId != null && chargerDto.getChargerCd() != null) {
chargerRatePlan.setRatePlanId((this.ratePlanId));
}
chargerRatePlan.setChargerCd(chargerDto.getChargerCd());
chargerRatePlan.setApplyStartDate(chargerDto.getApplyStartDate());
chargerRatePlan.setApplyEndDate(chargerDto.getApplyEndDate());
chargerRatePlan.setIsActive(chargerDto.getIsActive());
chargerRatePlanList.add(chargerRatePlan);
}
return chargerRatePlanList;
}
@Getter
@Setter
@ToString
public static class DetailsTimeDto {
private String seasonType;
private List<TimeRateDto> timeRates;
}
@Getter
@Setter
@ToString
public static class TimeRateDto {
private Integer id;
private String startTime;
private String endTime;
private Double price;
}
@Getter
@Setter
@ToString
public static class ChargerListDto {
private String chargerCd;
private String applyStartDate;
private String applyEndDate;
private String isActive;
}
}
@@ -0,0 +1,231 @@
package net.jwsi.jcms.vpp.ratePlan;
import jakarta.transaction.Transactional;
import lombok.extern.slf4j.Slf4j;
import net.jwsi.jcms.base.BaseService;
import net.jwsi.jcms.base.EnumSqlType;
import net.jwsi.jcms.utils.HttpUtil;
import net.jwsi.jcms.utils.LoginUtil;
import net.jwsi.jcms.vpp.RatePlanDetailTime.RatePlanDetailTime;
import net.jwsi.jcms.vpp.RatePlanDetailTime.RatePlanDetailTimeRepository;
import net.jwsi.jcms.vpp.api.ApiClient;
import net.jwsi.jcms.vpp.api.ApiResponse;
import net.jwsi.jcms.vpp.chargerRatePlan.ChargerRatePlan;
import net.jwsi.jcms.vpp.chargerRatePlan.ChargerRatePlanRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
@Service
@Slf4j
public class RatePlanService extends BaseService<RatePlan, Integer, RatePlanRepository> {
private final ApiClient apiClient;
private final RatePlanRepository ratePlanRepository;
private final RatePlanDetailTimeRepository ratePlanDetailTimeRepository;
private final ChargerRatePlanRepository chargerRatePlanRepository;
public RatePlanService(RatePlanRepository repository, ApiClient apiClient, RatePlanRepository ratePlanRepository, RatePlanDetailTimeRepository ratePlanDetailTimeRepository, ChargerRatePlanRepository chargerRatePlanRepository) {
super(repository);
this.apiClient = apiClient;
this.ratePlanRepository = ratePlanRepository;
this.ratePlanDetailTimeRepository = ratePlanDetailTimeRepository;
this.chargerRatePlanRepository = chargerRatePlanRepository;
}
@Transactional
@Scheduled(cron = "0 0 0 * * *")
public void getRatePlan() {
ApiResponse<List<RatePlanResponseDto>> response = apiClient.fetchRatePlan();
if (response == null || !Integer.valueOf(200).equals(response.getCode())) {
String msg = (response != null) ? response.getMessage() : "응답 없음";
throw new RuntimeException("API 호출 실패: " + msg);
}
List<RatePlanResponseDto> dtoList = response.getData();
if (dtoList == null) {
dtoList = new ArrayList<>();
}
Set<Integer> validApiIds = dtoList.stream()
.map(RatePlanResponseDto::getRatePlanId)
.collect(Collectors.toSet());
List<RatePlan> localRatePlans = ratePlanRepository.findAll();
int deleteCount = 0;
for (RatePlan localPlan : localRatePlans) {
if (!validApiIds.contains(localPlan.getRatePlanId())) {
Integer idToDelete = localPlan.getRatePlanId();
ratePlanDetailTimeRepository.deleteByRatePlanId(idToDelete);
chargerRatePlanRepository.deleteByRatePlanId(idToDelete);
ratePlanRepository.deleteById(idToDelete);
deleteCount++;
log.info("운영 DB에서 삭제된 요금제를 로컬 DB에서 제거했습니다. - ID: {}", idToDelete);
}
}
if (dtoList.isEmpty()) {
log.info("가져온 요금제 데이터가 없습니다. (로컬 동기화 및 삭제 완료. 삭제 건수: {})", deleteCount);
return;
}
List<RatePlan> ratePlanEntities = new ArrayList<>();
List<RatePlanDetailTime> allDetailTimes = new ArrayList<>();
List<ChargerRatePlan> allChargerRatePlans = new ArrayList<>();
Set<Integer> processedIds = new HashSet<>();
for (RatePlanResponseDto dto : dtoList) {
Integer currentRatePlanId = dto.getRatePlanId();
if (processedIds.contains(currentRatePlanId)) {
continue;
}
processedIds.add(currentRatePlanId);
RatePlan entity = ratePlanRepository.findById(currentRatePlanId)
.orElseGet(() -> {
RatePlan newEntity = new RatePlan();
newEntity.setRatePlanId(currentRatePlanId);
return newEntity;
});
entity.setRatePlanName(dto.getRatePlanName());
entity.setRatePlanType(dto.getRatePlanType());
entity.setSeasonType(dto.getSeasonType());
entity.setApplyStartDate(dto.getApplyStartDate());
entity.setApplyEndDate(dto.getApplyEndDate());
entity.setFixedPrice(dto.getFixedPrice());
entity.setApprovalStatus(dto.getApprovalStatus());
entity.setIsActive(dto.getIsActive());
ratePlanEntities.add(entity);
allDetailTimes.addAll(dto.toDetailTimeEntities());
allChargerRatePlans.addAll(dto.toChargerRatePlanEntities());
ratePlanDetailTimeRepository.deleteByRatePlanId(currentRatePlanId);
chargerRatePlanRepository.deleteByRatePlanId(currentRatePlanId);
}
ratePlanRepository.saveAll(ratePlanEntities);
ratePlanDetailTimeRepository.flush();
chargerRatePlanRepository.flush();
if (!allDetailTimes.isEmpty()) {
ratePlanDetailTimeRepository.saveAll(allDetailTimes);
}
if (!allChargerRatePlans.isEmpty()) {
chargerRatePlanRepository.saveAll(allChargerRatePlans);
}
log.info("요금제 동기화 완료: 마스터 추가/수정 {}건, 삭제 {}건, 상세단가 {}건, 충전기매핑 {}건",
ratePlanEntities.size(), deleteCount, allDetailTimes.size(), allChargerRatePlans.size());
}
public RatePlanResponseDto getRatePlanDetail(Integer ratePlanId) {
RatePlan ratePlan = ratePlanRepository.findById(ratePlanId)
.orElseThrow(() -> new RuntimeException("해당 요금제를 찾을 수 없습니다. ID: " + ratePlanId));
RatePlanResponseDto dto = new RatePlanResponseDto();
dto.setRatePlanId(ratePlan.getRatePlanId());
dto.setRatePlanName(ratePlan.getRatePlanName());
dto.setRatePlanType(ratePlan.getRatePlanType());
dto.setSeasonType(ratePlan.getSeasonType());
dto.setApplyStartDate(ratePlan.getApplyStartDate());
dto.setApplyEndDate(ratePlan.getApplyEndDate());
dto.setFixedPrice(ratePlan.getFixedPrice());
dto.setIsActive(ratePlan.getIsActive());
dto.setApprovalStatus(ratePlan.getApprovalStatus());
dto.setDescription(ratePlan.getDescription());
List<RatePlanDetailTime> detailTimes = ratePlanDetailTimeRepository.findByRatePlanId(ratePlanId);
Map<String, List<RatePlanDetailTime>> groupedBySeason = detailTimes.stream()
.collect(Collectors.groupingBy(RatePlanDetailTime::getSeasonType));
List<RatePlanResponseDto.DetailsTimeDto> detailsTimeDtos = new ArrayList<>();
for (Map.Entry<String, List<RatePlanDetailTime>> entry : groupedBySeason.entrySet()) {
RatePlanResponseDto.DetailsTimeDto seasonDto = new RatePlanResponseDto.DetailsTimeDto();
seasonDto.setSeasonType(entry.getKey());
seasonDto.setTimeRates(entry.getValue().stream().map(dt -> {
RatePlanResponseDto.TimeRateDto tr = new RatePlanResponseDto.TimeRateDto();
tr.setId(dt.getTimeSlotId());
tr.setPrice(dt.getPrice());
return tr;
}).collect(Collectors.toList()));
detailsTimeDtos.add(seasonDto);
}
dto.setDetailsTime(detailsTimeDtos);
List<ChargerRatePlan> chargerRatePlans = chargerRatePlanRepository.findByRatePlanId(ratePlanId);
log.info("🚨 [디버깅] 조회된 충전기 매핑 개수: {}", chargerRatePlans.size());
List<RatePlanResponseDto.ChargerListDto> chargerListDtos = chargerRatePlans.stream().map(cp -> {
RatePlanResponseDto.ChargerListDto cpDto = new RatePlanResponseDto.ChargerListDto();
cpDto.setChargerCd(cp.getChargerCd());
cpDto.setApplyStartDate(cp.getApplyStartDate());
cpDto.setApplyEndDate(cp.getApplyEndDate());
cpDto.setIsActive(cp.getIsActive());
return cpDto;
}).collect(Collectors.toList());
dto.setChargerList(chargerListDtos);
return dto;
}
@Override
protected Integer getUserId() {
return HttpUtil.getUserId();
}
@Override
protected String getUserName(Integer id) {
return LoginUtil.getUserName(id);
}
@Override
protected RatePlan newSearchParam(RatePlan ratePlan) {
if (ratePlan == null) {
ratePlan = new RatePlan();
}
return ratePlan;
}
@Override
protected EnumSqlType useSqlTyp() {
return EnumSqlType.SPECIFICATION;
}
@Override
protected Specification<RatePlan> getSpecification(RatePlan ratePlan) {
Specification<RatePlan> rst = null;
if(ratePlan == null) return null;
rst = addWhere(rst, ratePlan.getRatePlanName(), RatePlanSpecification.getRatePlanNm(ratePlan.getRatePlanName()));
rst = addWhere(rst, ratePlan.getApprovalStatus(), RatePlanSpecification.getApprovalStatus(ratePlan.getApprovalStatus()));
rst = addWhere(rst, ratePlan.getIsActive(), RatePlanSpecification.getIsActive(ratePlan.getIsActive()));
return rst;
}
@Override
protected Page<RatePlan> _selectPage(RatePlan ratePlan) {
return null;
}
@Override
protected List<RatePlan> _selectList(RatePlan ratePlan) {
return null;
}
}
@@ -0,0 +1,23 @@
package net.jwsi.jcms.vpp.ratePlan;
import net.jwsi.jcms.base.BaseSpecification;
import org.springframework.data.jpa.domain.Specification;
public class RatePlanSpecification extends BaseSpecification {
public static Specification<RatePlan> getRatePlanNm(String ratePlanName) {
return (root, query, cb) -> {
return cb.like(root.get("ratePlanName"), "%"+ratePlanName+"%");
};
}
public static Specification<RatePlan> getApprovalStatus(String approvalStatus) {
return (root, query, cb) -> {
return cb.equal(root.get("approvalStatus"), approvalStatus);
};
}
public static Specification<RatePlan> getIsActive(String isActive) {
return (root, query, cb) -> {
return cb.equal(root.get("isActive"), isActive);
};
}
}
@@ -1,4 +1,4 @@
package net.jwsi.jcms.vpp.rehchgingList; package net.jwsi.jcms.vpp.rechgingList;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.Getter; import lombok.Getter;
@@ -1,4 +1,4 @@
package net.jwsi.jcms.vpp.rehchgingList; package net.jwsi.jcms.vpp.rechgingList;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.jwsi.jcms.base.BaseController; import net.jwsi.jcms.base.BaseController;
@@ -14,7 +14,7 @@ import java.util.Map;
@Controller @Controller
@Slf4j @Slf4j
@RequestMapping("/cms/rechgingList") @RequestMapping("/cms/rechgingList/")
public class RechgingListController extends BaseController<RechgingList, Long, RechgingListRepository, RechgingListService> { public class RechgingListController extends BaseController<RechgingList, Long, RechgingListRepository, RechgingListService> {
private final String path = "/system/rechgingList/"; private final String path = "/system/rechgingList/";
@@ -1,4 +1,4 @@
package net.jwsi.jcms.vpp.rehchgingList; package net.jwsi.jcms.vpp.rechgingList;
import lombok.*; import lombok.*;
@@ -1,4 +1,4 @@
package net.jwsi.jcms.vpp.rehchgingList; package net.jwsi.jcms.vpp.rechgingList;
import net.jwsi.jcms.base.BaseRecStRepository; import net.jwsi.jcms.base.BaseRecStRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@@ -1,4 +1,4 @@
package net.jwsi.jcms.vpp.rehchgingList; package net.jwsi.jcms.vpp.rechgingList;
import jakarta.transaction.Transactional; import jakarta.transaction.Transactional;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -8,13 +8,11 @@ import net.jwsi.jcms.utils.HttpUtil;
import net.jwsi.jcms.utils.LoginUtil; import net.jwsi.jcms.utils.LoginUtil;
import net.jwsi.jcms.vpp.api.ApiClient; import net.jwsi.jcms.vpp.api.ApiClient;
import net.jwsi.jcms.vpp.api.ApiResponse; import net.jwsi.jcms.vpp.api.ApiResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -30,7 +28,7 @@ public class RechgingListService extends BaseService<RechgingList, Long, Rechgin
} }
@Transactional @Transactional
@Scheduled(cron = "0 0 0 * * *") @Scheduled(cron = "0 0/15 * * * *")
public void getRechgingList() { public void getRechgingList() {
ApiResponse<List<RechgingListDto>> response = apiClient.fetchRechgingList(); ApiResponse<List<RechgingListDto>> response = apiClient.fetchRechgingList();
@@ -82,9 +80,19 @@ public class RechgingListService extends BaseService<RechgingList, Long, Rechgin
@Override @Override
protected Specification<RechgingList> getSpecification(RechgingList srch) { protected Specification<RechgingList> getSpecification(RechgingList srch) {
if (srch == null) return null; if (srch == null) return null;
Specification<RechgingList> rst = null; Specification<RechgingList> rst = null;
rst = addWhere(rst, srch.getRechgingListId(), RechgingListSpecification.rechgingListId(srch.getRechgingListId())); rst = addWhere(rst, srch.getProviderId(), RechgingListSpecification.providerId(srch.getProviderId()));
rst = addWhere(rst, srch.getStId(), RechgingListSpecification.stId(srch.getStId()));
rst = addWhere(rst, srch.getChgrId(), RechgingListSpecification.chgrId(srch.getChgrId()));
rst = addWhere(rst, srch.getPlugType(), RechgingListSpecification.plugType(srch.getPlugType()));
rst = addWhere(rst, srch.getMemAuthInputNo(), RechgingListSpecification.memAuthInputNo(srch.getMemAuthInputNo()));
rst = addWhere(rst, srch.getCreditPPayTrxNo(), RechgingListSpecification.creditPPayTrxNo(srch.getCreditPPayTrxNo()));
rst = addWhere(rst, srch.getCreditPPayTrxDt(), RechgingListSpecification.creditPPayTrxDt(srch.getCreditPPayTrxDt()));
rst = addWhere(rst, srch.getRechgSdt(), RechgingListSpecification.rechgSdtBetween(srch.getRechgSdt()));
rst = addWhere(rst, srch.getPayType(), RechgingListSpecification.payType(srch.getPayType()));
return rst; return rst;
} }
@Override @Override
@@ -0,0 +1,57 @@
package net.jwsi.jcms.vpp.rechgingList;
import net.jwsi.jcms.base.BaseSpecification;
import net.jwsi.jcms.utils.StrUtil;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.StringUtils;
public class RechgingListSpecification extends BaseSpecification {
public static Specification<RechgingList> providerId(String providerId) {
return (root, query, cb) -> cb.like(root.get("providerId"), StrUtil.sqlLikeParam(providerId));
}
public static Specification<RechgingList> stId(String stId) {
return (root, query, cb) -> cb.like(root.get("stId"), StrUtil.sqlLikeParam(stId));
}
public static Specification<RechgingList> chgrId(String chgrId) {
return (root, query, cb) -> cb.like(root.get("chgrId"), StrUtil.sqlLikeParam(chgrId));
}
public static Specification<RechgingList> plugType(String plugType) {
return (root, query, cb) -> cb.like(root.get("plugType"), StrUtil.sqlLikeParam(plugType));
}
public static Specification<RechgingList> memAuthInputNo(String memAuthInputNo) {
return (root, query, cb) -> cb.like(root.get("memAuthInputNo"), StrUtil.sqlLikeParam(memAuthInputNo));
}
public static Specification<RechgingList> creditPPayTrxNo(String creditPPayTrxNo) {
return (root, query, cb) -> cb.like(root.get("creditPPayTrxNo"), StrUtil.sqlLikeParam(creditPPayTrxNo));
}
public static Specification<RechgingList> creditPPayTrxDt(String creditPPayTrxDt) {
return (root, query, cb) -> cb.like(root.get("creditPPayTrxDt"), StrUtil.sqlLikeParam(creditPPayTrxDt));
}
public static Specification<RechgingList> rechgSdtBetween(String rechgSdt) {
return (root, query, cb) -> {
// 날짜 파라미터가 없으면 검색 조건에서 제외 (null 반환)
if (!StringUtils.hasText(rechgSdt)) {
return null;
}
// 예: "2026-07-31" -> "2026-07-31 00:00:00" ~ "2026-07-31 23:59:59"
String startDateTime = rechgSdt.trim() + " 00:00:00";
String endDateTime = rechgSdt.trim() + " 23:59:59";
return cb.between(root.get("rechgSdt"), startDateTime, endDateTime);
};
}
public static Specification<RechgingList> payType(String payType) {
return (root, query, cb) -> cb.like(root.get("payType"), StrUtil.sqlLikeParam(payType));
}
}
@@ -1,11 +0,0 @@
package net.jwsi.jcms.vpp.rehchgingList;
import org.springframework.data.jpa.domain.Specification;
public class RechgingListSpecification {
public static Specification<RechgingList> rechgingListId(Long rechgingListId) {
return (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("rechgingListId"), rechgingListId);
}
}
@@ -77,7 +77,8 @@ public class StationService extends BaseService<Station,String,StationRepository
protected Specification<Station> getSpecification(Station srch) { protected Specification<Station> getSpecification(Station srch) {
if(srch == null) return null; if(srch == null) return null;
Specification<Station> rst = null; Specification<Station> rst = null;
rst = addWhere(rst, srch.getStationId(), StationSpecification.stId(srch.getStationId())); rst = addWhere(rst, srch.getStNm(), StationSpecification.stNm(srch.getStNm()));
rst = addWhere(rst, srch.getAddrLdM(), StationSpecification.addrLdM(srch.getAddrLdM()));
return rst; return rst;
} }
@@ -5,7 +5,10 @@ import org.springframework.data.jpa.domain.Specification;
public class StationSpecification { public class StationSpecification {
public static Specification<Station> stId(String stId) { public static Specification<Station> stNm(String stNm) {
return (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("stId"), stId); return (root, query, criteriaBuilder) -> criteriaBuilder.like(root.get("stNm"),"%"+stNm+"%");
}
public static Specification<Station> addrLdM(String addrLdM) {
return (root, query, criteriaBuilder) -> criteriaBuilder.like(root.get("addrLdM"),"%"+addrLdM+"%");
} }
} }
+2 -2
View File
@@ -15,8 +15,8 @@ spring:
jcms-db: jcms-db:
driver-class-name: org.mariadb.jdbc.Driver driver-class-name: org.mariadb.jdbc.Driver
jdbc-url: jdbc:mariadb://192.168.62.37:3306/jcmsdb jdbc-url: jdbc:mariadb://192.168.62.37:3306/jcmsdb
username: jcms username: root
password: jcms3593 password: jcms
server: server:
port: 8080 port: 8080
+2 -2
View File
@@ -9,9 +9,9 @@ spring:
datasource: datasource:
jcms-db: jcms-db:
driver-class-name: org.mariadb.jdbc.Driver driver-class-name: org.mariadb.jdbc.Driver
jdbc-url: jdbc:mariadb://192.168.62.37:33068/jcms jdbc-url: jdbc:mariadb://dev.jinwoosi.co.kr:33091/jcms
username: jcms username: jcms
password: ENC(nqt7VLhtxqwiJcQQw9ncNg==) password: jw3593
jpa: jpa:
entity-packages: net.jwsi.jcms entity-packages: net.jwsi.jcms
show-sql: false show-sql: false
@@ -6,12 +6,6 @@
<div th:replace="~{common/fragments/searchBox :: SearchBoxFragment(~{ :: #searchBoxContent}, ~{ :: #searchBoxFooter})}"> <div th:replace="~{common/fragments/searchBox :: SearchBoxFragment(~{ :: #searchBoxContent}, ~{ :: #searchBoxFooter})}">
<div id="searchBoxContent"> <div id="searchBoxContent">
<div class="row px-2"> <div class="row px-2">
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:140px">충전사업자 ID</span>
<input type="text" id="providerIdLike" name="providerIdLike" class="form-control" placeholder="충전사업자 ID 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1"> <div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group"> <div class="input-group">
<span class="input-group-text" style="width:140px">충전소 ID</span> <span class="input-group-text" style="width:140px">충전소 ID</span>
@@ -24,30 +18,6 @@
<input type="text" id="chgrIdLike" name="chgrIdLike" class="form-control" placeholder="충전기 ID 입력" maxlength="50"> <input type="text" id="chgrIdLike" name="chgrIdLike" class="form-control" placeholder="충전기 ID 입력" maxlength="50">
</div> </div>
</div> </div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:140px">충전소 명</span>
<input type="text" id="stNmLike" name="stNmLike" class="form-control" placeholder="충전소 명 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:140px">충전기 명</span>
<input type="text" id="chgrNmLike" name="chgrNmLike" class="form-control" placeholder="충전기 명 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:140px">충전기속도</span>
<input type="text" id="speedTpLike" name="speedTpLike" class="form-control" placeholder="충전기속도 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:140px">충전기타입</span>
<input type="text" id="chgrTpLike" name="chgrTpLike" class="form-control" placeholder="충전기타입 입력" maxlength="50">
</div>
</div>
</div> </div>
</div> </div>
<div id="searchBoxFooter"> <div id="searchBoxFooter">
@@ -8,54 +8,36 @@
<div class="row px-2"> <div class="row px-2">
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1"> <div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group"> <div class="input-group">
<span class="input-group-text" style="width:180px">provider_id</span> <span class="input-group-text" style="width:180px">충전소 아이디</span>
<input type="text" id="providerIdLike" name="providerIdLike" class="form-control" placeholder="provider_id 입력" maxlength="50"> <input type="text" id="stIdLike" name="stIdLike" class="form-control" placeholder="충전소 아이디 입력" maxlength="50">
</div> </div>
</div> </div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1"> <div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group"> <div class="input-group">
<span class="input-group-text" style="width:180px">st_id</span> <span class="input-group-text" style="width:180px">충전기 아이디</span>
<input type="text" id="stIdLike" name="stIdLike" class="form-control" placeholder="st_id 입력" maxlength="50"> <input type="text" id="chgrIdLike" name="chgrIdLike" class="form-control" placeholder="충전기 아이디 입력" maxlength="50">
</div> </div>
</div> </div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1"> <div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group"> <div class="input-group">
<span class="input-group-text" style="width:180px">chgr_id</span> <span class="input-group-text" style="width:180px">충전상태</span>
<input type="text" id="chgrIdLike" name="chgrIdLike" class="form-control" placeholder="chgr_id 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:180px">상태 일시</span>
<input type="text" id="stateDtLike" name="stateDtLike" class="form-control" placeholder="상태 일시 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:180px">op_mode</span>
<input type="text" id="opModeLike" name="opModeLike" class="form-control" placeholder="op_mode_cd 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:180px">ch1_rechg_state_cd</span>
<select id="ch1RechgStateCdLike" name="ch1RechgStateCdLike" class="form-control"></select> <select id="ch1RechgStateCdLike" name="ch1RechgStateCdLike" class="form-control"></select>
</div> </div>
</div> </div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1"> <div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group"> <div class="input-group">
<span class="input-group-text" style="width:180px">ch1_door_state_cd</span> <span class="input-group-text" style="width:180px">도어상태</span>
<select id="ch1DoorStateCdLike" name="ch1DoorStateCdLike" class="form-control"></select> <select id="ch1DoorStateCdLike" name="ch1DoorStateCdLike" class="form-control"></select>
</div> </div>
</div> </div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1"> <div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group"> <div class="input-group">
<span class="input-group-text" style="width:180px">ch1_plug_state_cd</span> <span class="input-group-text" style="width:180px">플러그상태</span>
<select id="ch1PlugStateCdLike" name="ch1PlugStateCdLike" class="form-control"></select> <select id="ch1PlugStateCdLike" name="ch1PlugStateCdLike" class="form-control"></select>
</div> </div>
</div> </div>
</div> <!-- row 닫힘 --> </div>
</div> <!-- searchBoxContent 닫힘 --> </div>
<div id="searchBoxFooter"> <div id="searchBoxFooter">
<div class="row float-end"> <div class="row float-end">
@@ -67,9 +49,9 @@
</th:block> </th:block>
</div> </div>
</div> </div>
</div> <!-- searchBoxFooter 닫힘 --> </div>
</div> <!-- SearchBoxFragment 닫힘 --> </div>
</div> <!-- w-100 닫힘 --> </div>
<div class="w-100"> <div class="w-100">
<div class="box box-primary"> <div class="box box-primary">
@@ -79,13 +61,6 @@
</div> </div>
</div> </div>
<!-- 권한 변수 선언 -->
<script th:inline="javascript">
const menuRoleWrite = /*[[${menuRole != null ? menuRole.roleWrite : false}]]*/ false;
const menuRoleModify = /*[[${menuRole != null ? menuRole.roleModify : false}]]*/ false;
const menuRoleDel = /*[[${menuRole != null ? menuRole.roleDel : false}]]*/ false;
</script>
<script th:inline="none"> <script th:inline="none">
const rootPath = "/cms/chgrCurrState/"; const rootPath = "/cms/chgrCurrState/";
let datatable; let datatable;
@@ -120,9 +95,7 @@
{title: "사업자 ID", name: "provider_id", data: "providerId", className}, {title: "사업자 ID", name: "provider_id", data: "providerId", className},
{title: "충전소 ID", name: "st_id", data: "stId", className}, {title: "충전소 ID", name: "st_id", data: "stId", className},
{title: "충전기 ID", name: "chgr_id", data: "chgrId", className}, {title: "충전기 ID", name: "chgr_id", data: "chgrId", className},
{title: "상태 일시", name: "state_dt", data: "stateDt", className, orderable: false},
{title: "운영모드", name: "op_mode", data: "opMode", className, orderable: false}, {title: "운영모드", name: "op_mode", data: "opMode", className, orderable: false},
{title: "CH1 상태", orderable: false, className, {title: "CH1 상태", orderable: false, className,
render: (data, type, row) => { render: (data, type, row) => {
let s1 = getCdDtlDtColNm(ch1RechgStateCdCdList, row["ch1RechgStateCd"]) || ""; let s1 = getCdDtlDtColNm(ch1RechgStateCdCdList, row["ch1RechgStateCd"]) || "";
@@ -131,6 +104,8 @@
return `<div class="d-flex justify-content-center gap-1">${s1} ${s2} ${s3}</div>`; return `<div class="d-flex justify-content-center gap-1">${s1} ${s2} ${s3}</div>`;
} }
}, },
{title: "상태 일시", name: "state_dt", data: "stateDt", className, orderable: false},
{title: "integrated_kwh", name: "integrated_kwh", data: "integratedKwh", className, visible: false}, {title: "integrated_kwh", name: "integrated_kwh", data: "integratedKwh", className, visible: false},
{title: "dc_power_wh", name: "dc_power_wh", data: "dcPowerWh", className, visible: false}, {title: "dc_power_wh", name: "dc_power_wh", data: "dcPowerWh", className, visible: false},
{title: "ac_power_wh", name: "ac_power_wh", data: "acPowerWh", className, visible: false}, {title: "ac_power_wh", name: "ac_power_wh", data: "acPowerWh", className, visible: false},
@@ -8,52 +8,16 @@
<div class="row px-2"> <div class="row px-2">
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1"> <div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group"> <div class="input-group">
<span class="input-group-text" style="width:160px">충전소 </span> <span class="input-group-text" style="width:160px">충전소 아이디</span>
<input type="text" id="stIdLike" name="stIdLike" class="form-control" placeholder="충전소 명 입력" maxlength="50"> <input type="text" id="stIdLike" name="stIdLike" class="form-control" placeholder="충전소 명 입력" maxlength="50">
</div> </div>
</div> </div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1"> <div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group"> <div class="input-group">
<span class="input-group-text" style="width:160px">충전기 </span> <span class="input-group-text" style="width:160px">충전기 아이디</span>
<input type="text" id="chgrIdLike" name="chgrIdLike" class="form-control" placeholder="충전기 명 입력" maxlength="50"> <input type="text" id="chgrIdLike" name="chgrIdLike" class="form-control" placeholder="충전기 명 입력" maxlength="50">
</div> </div>
</div> </div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:160px">결제구분</span>
<input type="text" id="payTpLike" name="payTpLike" class="form-control" placeholder="결제구분 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:160px">충전기타입 코드</span>
<input type="text" id="plugTpLike" name="plugTpLike" class="form-control" placeholder="충전기타입 코드 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:160px">충전 시작일시</span>
<input type="text" id="rechgSDtLike" name="rechgSDtLike" class="form-control" placeholder="충전 시작일시 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:160px">충전 종료일시</span>
<input type="text" id="rechgEDtLike" name="rechgEDtLike" class="form-control" placeholder="충전 종료일시 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:160px">충전시간</span>
<input type="text" id="rechgTimeLike" name="rechgTimeLike" class="form-control" placeholder="충전시간 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:160px">신용결제완료구분</span>
<input type="text" id="payFnshLike" name="payFnshLike" class="form-control" placeholder="신용결제완료구분 입력" maxlength="50">
</div>
</div>
</div> </div>
</div> </div>
<div id="searchBoxFooter"> <div id="searchBoxFooter">
@@ -96,15 +60,18 @@
if (strUtil.isNotEmpty($('#rechgEDtLike').val())) {d.rechgEDt = $('#rechgEDtLike').val();} if (strUtil.isNotEmpty($('#rechgEDtLike').val())) {d.rechgEDt = $('#rechgEDtLike').val();}
if (strUtil.isNotEmpty($('#rechgTimeLike').val())) {d.rechgTime = $('#rechgTimeLike').val();} if (strUtil.isNotEmpty($('#rechgTimeLike').val())) {d.rechgTime = $('#rechgTimeLike').val();}
if (strUtil.isNotEmpty($('#payFnshLike').val())) {d.payFnsh = $('#payFnshLike').val();} if (strUtil.isNotEmpty($('#payFnshLike').val())) {d.payFnsh = $('#payFnshLike').val();}
if (strUtil.isNotEmpty($('#chIdLike').val())) {d.chId = $('#chIdLike').val();}
return d; return d;
}, },
{ {
order: [], order: [[5, 'desc']],
columns: [ columns: [
{title: "충전소 ", name: "ST_NM", data: "stId", className, orderable: false}, {title: "충전소 아이디", name: "ST_ID", data: "stId", className, orderable: false},
{title: "충전기 ", name: "CHGR_NM", data: "chgrId", className, orderable: false}, {title: "충전기 아이디", name: "CHGR_ID", data: "chgrId", className, orderable: false},
{title: "결제구분", name: "PAY_TP", data: "payTp", className, orderable: false}, {title: "결제구분", name: "PAY_TP", data: "payTp", className, orderable: false},
{title: "충전기타입 코드", name: "CHGR_TP", data: "plugTp", className, orderable: false}, {title: "채널", name: "CH_ID", data: "chId", className, orderable: false},
{title: "충전기타입 코드", name: "PLUG_TP", data: "plugTp", className, orderable: false},
{title: "충전 시작일시", name: "RECHG_S_DT", data: "rechgSDt", className, orderable: false}, {title: "충전 시작일시", name: "RECHG_S_DT", data: "rechgSDt", className, orderable: false},
{title: "충전 종료일시", name: "RECHG_E_DT", data: "rechgEDt", className, orderable: false}, {title: "충전 종료일시", name: "RECHG_E_DT", data: "rechgEDt", className, orderable: false},
{title: "충전시간", name: "RECHG_TIME", data: "rechgTime", className, orderable: false}, {title: "충전시간", name: "RECHG_TIME", data: "rechgTime", className, orderable: false},
@@ -170,6 +137,17 @@
minLen: 0, minLen: 0,
maxLen: 50, maxLen: 50,
}), }),
new ModalInput({
inputType: ModalInputType.text,
inputId: "chId",
inputName: "chId",
inputLabel: "채널",
inputPlaceholder: "채널",
isReq: false,
isEnable: true,
minLen: 0,
maxLen: 50,
}),
new ModalInput({ new ModalInput({
inputType: ModalInputType.textarea, inputType: ModalInputType.textarea,
inputId: "payTp", inputId: "payTp",
@@ -283,12 +261,6 @@
$("#resetBtn").click(function() { $("#resetBtn").click(function() {
$("#stIdLike").val(""); $("#stIdLike").val("");
$("#chgrIdLike").val(""); $("#chgrIdLike").val("");
$("#payTpLike").val("");
$("#plugTpLike").val("");
$("#rechgSDtLike").val("");
$("#rechgEDtLike").val("");
$("#rechgTimeLike").val("");
$("#payFnshLike").val("");
datatable.ajax.reload(); datatable.ajax.reload();
}); });
</script> </script>
@@ -12,30 +12,6 @@
<input type="text" id="providerNmLike" name="providerNmLike" class="form-control" placeholder="충전사업자 명 입력" maxlength="50"> <input type="text" id="providerNmLike" name="providerNmLike" class="form-control" placeholder="충전사업자 명 입력" maxlength="50">
</div> </div>
</div> </div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:140px">사업자번호</span>
<input type="text" id="bizTaxIdLike" name="bizTaxIdLike" class="form-control" placeholder="사업자번호 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:140px">대표자</span>
<input type="text" id="ceoLike" name="ceoLike" class="form-control" placeholder="대표자 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:140px">전화번호</span>
<input type="text" id="telLike" name="telLike" class="form-control" placeholder="전화번호 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:140px">팩스번호</span>
<input type="text" id="faxLike" name="faxLike" class="form-control" placeholder="팩스번호 입력" maxlength="50">
</div>
</div>
</div> </div>
</div> </div>
<div id="searchBoxFooter"> <div id="searchBoxFooter">
@@ -281,10 +257,6 @@
$("#resetBtn").click(function() { $("#resetBtn").click(function() {
$("#providerNmLike").val(""); $("#providerNmLike").val("");
$("#bizTaxIdLike").val("");
$("#ceoLike").val("");
$("#telLike").val("");
$("#faxLike").val("");
datatable.ajax.reload(); datatable.ajax.reload();
}); });
</script> </script>
@@ -0,0 +1,573 @@
<!DOCTYPE html>
<html xmlns:th="http://thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/cmsLayout}">
<body>
<section layout:fragment="Content">
<div class="w-100" style="margin-bottom: .3rem;">
<div th:replace="~{common/fragments/searchBox :: SearchBoxFragment(~{ :: #searchBoxContent}, ~{ :: #searchBoxFooter})}">
<div id="searchBoxContent">
<div class="row px-2">
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:120px">요금제명</span>
<input type="text" id="ratePlanNameLike" name="ratePlanNameLike" class="form-control" placeholder="요금제명 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:120px">승인 상태</span>
<select id="approvalStatusLike" name="approvalStatusLike" class="form-control">
<option value="">전체</option>
<option value="approved">승인완료</option>
<option value="pending">대기중</option>
</select>
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:120px">활성화 여부</span>
<select id="isActiveLike" name="isActiveLike" class="form-control">
<option value="">전체</option>
<option value="true">활성</option>
<option value="false">비활성</option>
</select>
</div>
</div>
</div>
</div>
<div id="searchBoxFooter">
<div class="row float-end">
<div class="col-xs-12 ">
<button type="button" class="btn btn-sm btn-info" id="searchBtn">검색</button>
<button type="button" class="btn btn-sm btn-info" id="resetBtn">리셋</button>
<th:block th:if="${menuRole?.roleWrite eq true}">
<button type="button" class="btn btn-sm btn-primary pl-2" onclick="showModal(ModalMode.REGISTER, {}, '');">등록</button>
</th:block>
</div>
</div>
</div>
</div>
</div>
<div class="w-100">
<div class="box box-primary">
<div class="form">
<table id="datatable" class="table table-striped table-hover dataTable" style="width:100%;"></table>
</div>
</div>
</div>
<style>
#ratePlanDetailScrollArea { max-height: 65vh; overflow-y: auto; overflow-x: hidden; padding-right: 5px; }
#ratePlanDetailScrollArea::-webkit-scrollbar { width: 6px; }
#ratePlanDetailScrollArea::-webkit-scrollbar-track { background: #f5f5f5; border-radius: 3px; }
#ratePlanDetailScrollArea::-webkit-scrollbar-thumb { background: #d0d0d0; border-radius: 3px; }
#ratePlanDetailScrollArea::-webkit-scrollbar-thumb:hover { background: #b5b5b5; }
.custom-card { border: 1px solid #e5e5e5; border-radius: 0.25rem; margin-bottom: 1rem; background-color: #fff; }
.custom-card-header { color: #333; font-weight: 600; font-size: 0.88rem; padding: 0.6rem 1rem; border-bottom: 1px solid #e5e5e5; background-color: #fafafa; }
.custom-card-body { padding: 0.75rem 1rem; }
.info-table th, .info-table td { padding: 0.2rem 0.4rem !important; font-size: 0.85rem; color: #444; }
.info-table th { font-weight: 600; width: 110px; color: #222; white-space: nowrap; }
.info-table td { width: 100%; }
.price-table { font-size: 0.85rem; border-color: #e5e5e5; }
.price-table th, .price-table td { border-color: #e5e5e5; padding: 0.5rem; }
.price-table thead th { border-bottom-width: 1px; font-weight: 600; color: #555; }
.price-table thead th:first-child { color: #222; }
.val-price { color: #222; font-weight: 700; }
.val-unit { color: #999; font-size: 0.8rem; }
.charger-item { border: 1px solid #e5e5e5; border-radius: 0.35rem; padding: 0.6rem; background-color: #fff; height: 100%; }
.charger-cd { font-size: 0.9rem; font-weight: bold; color: #222; margin-bottom: 0.1rem; }
.charger-dt { font-size: 0.75rem; color: #aaa; margin-bottom: 0.4rem; }
.badge-square { background-color: #f0f0f0; color: #333; border: 1px solid #d8d8d8; padding: 0.15rem 0.4rem; font-size: 0.7rem; border-radius: 2px; font-weight: 500; }
.badge-square.active { background-color: #eef1fb; color: #3b5bdb; border-color: #d6dcf5; }
.approval-ok { color: #333; font-weight: 600; }
.approval-ok i { color: #3b5bdb; }
.season-tabs { display: flex; border-bottom: 1px solid #e5e5e5; margin-bottom: 0.75rem; }
.season-tab-btn { flex: 1; text-align: center; padding: 0.5rem 0; cursor: pointer; font-size: 0.85rem; color: #666; border-bottom: 2px solid transparent; background: none; border-top:none; border-left:none; border-right:none; }
.season-tab-btn.active { color: #3b5bdb; font-weight: 600; border-bottom-color: #3b5bdb; }
.time-mode-toggle { display: flex; border: 1px solid #e5e5e5; border-radius: 0.25rem; overflow: hidden; margin-bottom: 0.75rem; }
.time-mode-btn { flex: 1; text-align: center; padding: 0.4rem 0; cursor: pointer; font-size: 0.82rem; color: #666; background: #fafafa; }
.time-mode-btn.active { background: #3b5bdb; color: #fff; font-weight: 600; }
.basic-price-row { display: flex; align-items: center; justify-content: space-between; padding: 0.5rem 0; border-bottom: 1px solid #f2f2f2; }
.basic-price-row:last-child { border-bottom: none; }
.basic-price-label { font-size: 0.85rem; color: #333; }
.basic-price-input { width: 140px; text-align: right; }
.detail-price-row { display: flex; align-items: center; justify-content: space-between; padding: 0.4rem 0; border-bottom: 1px solid #f5f5f5; }
.detail-price-label { font-size: 0.82rem; color: #444; }
.detail-price-input { width: 140px; text-align: right; }
.detail-scroll { max-height: 260px; overflow-y: auto; padding-right: 4px; }
.charger-search-list { max-height: 260px; overflow-y: auto; border: 1px solid #eee; border-radius: 0.25rem; padding: 0.4rem; }
.charger-check-item { display: flex; align-items: flex-start; gap: 0.5rem; padding: 0.5rem; border: 1px solid #eee; border-radius: 0.3rem; margin-bottom: 0.4rem; background:#fff; }
.charger-check-item label { margin: 0; font-size: 0.82rem; }
</style>
<script th:inline="none">
const rootPath = "/cms/ratePlan/";
let datatable;
const BASIC_SLOTS = [
{ id: 1, label: "경부하 (22:00 - 08:00)", hours: [22,23,0,1,2,3,4,5,6,7] },
{ id: 2, label: "중간부하 (08:00 - 16:00)", hours: [8,9,10,11,12,13,14,15] },
{ id: 3, label: "최대부하 (16:00 - 22:00)", hours: [16,17,18,19,20,21] }
];
const SEASON_LABEL = { spring:"봄", summer:"여름", fall:"가을", winter:"겨울", all_year:"연중" };
$(document).ready(function() {
const className = "dt-head-center dt-body-center";
datatable = newDataTable(
"#datatable",
rootPath + "list.json",
function(d) {
if (strUtil.isNotEmpty($('#ratePlanNameLike').val())) {d.ratePlanName = $('#ratePlanNameLike').val();}
if (strUtil.isNotEmpty($('#approvalStatusLike').val())) {d.approvalStatus = $('#approvalStatusLike').val();}
if (strUtil.isNotEmpty($('#isActiveLike').val())) {d.isActive = $('#isActiveLike').val();}
return d;
},
{
order: [[0, 'desc']],
columns: [
{title: "요금제 ID", name: "RATE_PLAN_ID", data: "ratePlanId", className, orderable:false, width: "80px"},
{title: "요금제명", name: "RATE_PLAN_NAME", data: "ratePlanName", orderable:false, className: "dt-head-center dt-body-left"},
{title: "요금제 구분", name: "RATE_PLAN_TYPE", data: "ratePlanType", orderable:false, className, render: (data) => data === 'time' ? '계시별 요금제' : data},
{title: "적용 시작일", name: "APPLY_START_DATE", data: "applyStartDate", orderable:false, className, width: "120px"},
{title: "적용 종료일", name: "APPLY_END_DATE", data: "applyEndDate", orderable:false, className, width: "120px"},
{title: "승인 상태", name: "APPROVAL_STATUS", data: "approvalStatus", orderable:false, className, width: "100px", render: (data) => data === 'approved' ? '<span class="text-success fw-bold"><i class="bi bi-check-lg"></i> 승인완료</span>' : '<span class="text-secondary">대기중</span>'},
{title: "활성화", name: "IS_ACTIVE", data: "isActive", orderable:false, className, width: "80px", render: (data) => {
let val = String(data || '').toLowerCase();
return (val === 'true' || val === 'y') ? '<span class="text-primary fw-bold">활성</span>' : '<span class="text-secondary">비활성</span>';
}},
{title: "기능", orderable:false, width:"120px", render: (data, type, row) => mkRowDataFunctions({ratePlanId: row['ratePlanId']}, row['ratePlanName'], true, true, true)}
],
allCheck: false,
}
);
});
const showModal = (mode, ids, title) => {
if(!checkRole(mode)) { alert("권한이 없습니다."); return; }
const loadUrl = rootPath + "view.json";
const modalWidth = 780;
const labelWidth = '1px';
if(mode === ModalMode.VIEW || mode === ModalMode.VIEW_ONLY) {
let viewInputArray = [
new ModalInput({
inputType: ModalInputType.customer,
inputId: "detailViewTemplate",
inputName: "detailViewTemplate",
options: () => {
return `
<div class="col-12 p-0" id="ratePlanDetailScrollArea">
<div class="custom-card">
<div class="custom-card-header">기본 정보</div>
<div class="custom-card-body">
<table class="table table-borderless mb-0 info-table">
<tr><th>요금제명:</th><td id="v_ratePlanName">-</td></tr>
<tr><th>요금제 구분:</th><td id="v_ratePlanType">-</td></tr>
<tr><th>계절 구분:</th><td id="v_seasonType">-</td></tr>
<tr><th>적용 기간:</th><td id="v_applyDate">-</td></tr>
<tr><th>활성화 상태:</th><td id="v_isActive">-</td></tr>
<tr><th>설명:</th><td id="v_description">-</td></tr>
</table>
</div>
</div>
<div class="custom-card">
<div class="custom-card-header">승인 정보</div>
<div class="custom-card-body">
<table class="table table-borderless mb-0 info-table">
<tr><th>승인 상태:</th><td id="v_approvalStatus">-</td></tr>
</table>
</div>
</div>
<div class="custom-card">
<div class="custom-card-header">시간대별 단가 설정</div>
<div class="custom-card-body p-0" id="v_priceTableArea"></div>
</div>
<div class="custom-card mb-1">
<div class="custom-card-header" style="border-bottom: 1px solid #e5e5e5;">적용 충전기 목록 (<span id="v_chargerCount">0</span>개)</div>
<div class="custom-card-body pt-3 pb-2" style="background-color: #fcfcfc;">
<div class="row g-2" id="v_chargerListArea"></div>
</div>
</div>
</div>
`;
}
})
];
newModal(new ModalInfo({modalTitle: "요금제 상세보기", inputArray: viewInputArray, modalWidth: modalWidth}), mode, 'main', {
loadUrl: loadUrl,
ids: ids,
labelWidth: labelWidth,
callInit: function(param1, param2, param3) {
let data;
if (param1 && param1.ratePlanId) data = param1;
else if (param2 && param2.ratePlanId) data = param2;
else if (param1 && param1.data) data = param1.data;
else if (param2 && param2.data) data = param2.data;
if(!data) { return; }
setTimeout(() => {
$('#v_ratePlanName').text(data.ratePlanName || '-');
$('#v_ratePlanType').text(data.ratePlanType === 'time' ? '계시별 요금제' : data.ratePlanType);
$('#v_seasonType').text(data.seasonType === 'season' ? '계절별' : '연중');
$('#v_applyDate').text(`${data.applyStartDate || ''} ~ ${data.applyEndDate || ''}`);
let mainActiveVal = String(data.isActive || data.active || '').toLowerCase();
$('#v_isActive').text((mainActiveVal === 'true' || mainActiveVal === 'y') ? '활성화' : '비활성');
$('#v_description').text(data.description || '-');
if(data.approvalStatus === 'approved') {
$('#v_approvalStatus').html('<span class="approval-ok"><i class="bi bi-check-lg"></i> 승인완료</span>');
} else {
$('#v_approvalStatus').text(data.approvalStatus || '대기중');
}
renderViewPriceTable(data.detailsTime || []);
if (data.chargerList && data.chargerList.length > 0) {
$('#v_chargerCount').text(data.chargerList.length);
let chargerHtml = data.chargerList.map(c => {
let isActiveVal = String(c.isActive || c.active || '').toLowerCase();
let isActive = (isActiveVal === 'true' || isActiveVal === 'y');
let badgeClass = isActive ? 'badge-square active' : 'badge-square';
let badgeText = isActive ? '적용중' : '중지';
return `
<div class="col-6 col-sm-4 mb-2">
<div class="charger-item">
<div class="charger-cd"><i class="bi bi-ev-front"></i> ${c.chargerCd}</div>
<div class="charger-dt">${c.applyStartDate || ''} ~ ${c.applyEndDate || ''}</div>
<div><span class="${badgeClass}">${badgeText}</span></div>
</div>
</div>
`}).join('');
$('#v_chargerListArea').html(chargerHtml);
} else {
$('#v_chargerCount').text('0');
$('#v_chargerListArea').html('<div class="col-12 text-center text-muted py-4">적용된 충전기가 없습니다.</div>');
}
}, 200);
}
});
}
else if(mode === ModalMode.REGISTER) {
let registerInputArray = [
new ModalInput({
inputType: ModalInputType.customer,
inputId: "registerViewTemplate",
options: () => {
return `
<div class="col-12 p-0" id="ratePlanDetailScrollArea">
<div class="custom-card">
<div class="custom-card-header">기본 정보</div>
<div class="custom-card-body">
<table class="table table-borderless mb-0 info-table">
<tr><th>요금제명:</th><td><input type="text" id="i_ratePlanName" class="form-control form-control-sm" placeholder="예: 2026년 계시별 요금제"></td></tr>
<tr><th>요금제 구분:</th><td><select id="i_ratePlanType" class="form-control form-control-sm" disabled><option value="time" selected>계시별</option></select></td></tr>
<tr><th>계절 선택:</th><td>
<select id="i_seasonType" class="form-control form-control-sm">
<option value="season" selected>계절별</option>
<option value="all_year">연중</option>
</select>
</td></tr>
<tr><th>적용 시작일:</th><td><input type="date" id="i_applyStartDate" class="form-control form-control-sm"></td></tr>
<tr><th>적용 종료일:</th><td><input type="date" id="i_applyEndDate" class="form-control form-control-sm"></td></tr>
<tr><th>요금제 설명:</th><td><textarea id="i_description" class="form-control form-control-sm" rows="2"></textarea></td></tr>
</table>
</div>
</div>
<div class="custom-card">
<div class="custom-card-header">계시별 단가 설정</div>
<div class="custom-card-body">
<div id="i_seasonTabsArea"></div>
<div id="i_pricePanelsArea"></div>
</div>
</div>
<div class="custom-card mb-1">
<div class="custom-card-header">적용할 충전기 선택</div>
<div class="custom-card-body">
<div class="d-flex gap-2 mb-2">
<input type="text" id="i_chargerSearch" class="form-control form-control-sm" placeholder="충전기명 또는 위치로 검색...">
<select id="i_chargerFilter" class="form-control form-control-sm" style="max-width:120px;">
<option value="">전체</option>
<option value="완속">완속</option>
<option value="급속">급속</option>
<option value="초급속">초급속</option>
</select>
</div>
<div class="mb-2">
<input type="checkbox" id="i_chargerAll"> <label for="i_chargerAll" class="ms-1">전체 선택 (<span id="i_chargerAllCount">0</span>개)</label>
</div>
<div class="charger-search-list" id="i_chargerListArea"></div>
</div>
</div>
</div>
`;
}
})
];
newModal(new ModalInfo({modalTitle: "새 단가 정책 생성", inputArray: registerInputArray, modalWidth: modalWidth}), mode, 'main', {
labelWidth: '1px',
callInit: function() {
setTimeout(() => {
initRegisterForm();
$('.modal-footer button[name="approve"]').off('click').on('click', function(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
if(confirm("등록하시겠습니까?")) {
submitRegisterForm();
}
});
}, 200);
}
});
}
};
let priceState = {};
let activeSeason = null;
function getSeasons() {
return $('#i_seasonType').val() === 'season' ? ['spring','summer','fall','winter'] : ['all_year'];
}
function initPriceStateFor(seasonKey) {
if (!priceState[seasonKey]) {
let basic = {}; BASIC_SLOTS.forEach(s => basic[s.id] = 0);
let detail = {}; for (let h = 0; h < 24; h++) detail[h] = 0;
priceState[seasonKey] = { mode: 'basic', basic, detail };
}
}
function initRegisterForm() {
priceState = {};
$('#i_seasonType').off('change').on('change', renderSeasonTabsAndPanel);
renderSeasonTabsAndPanel();
loadChargerList();
$('#i_chargerSearch, #i_chargerFilter').off('input change').on('input change', filterChargerList);
$('#i_chargerAll').off('change').on('change', function() {
$('#i_chargerListArea .charger-check-cb:visible').prop('checked', $(this).is(':checked'));
updateAllCheckState();
});
}
function renderSeasonTabsAndPanel() {
const seasons = getSeasons();
seasons.forEach(initPriceStateFor);
activeSeason = seasons[0];
let tabsHtml = '';
if (seasons.length > 1) {
tabsHtml = `<div class="season-tabs">` +
seasons.map((s, i) => `<button type="button" class="season-tab-btn ${i===0?'active':''}" data-season="${s}">${SEASON_LABEL[s]}</button>`).join('') +
`</div>`;
}
$('#i_seasonTabsArea').html(tabsHtml);
$('.season-tab-btn').off('click').on('click', function() {
saveCurrentPanelValues(activeSeason);
$('.season-tab-btn').removeClass('active');
$(this).addClass('active');
activeSeason = $(this).data('season');
renderPricePanel(activeSeason);
});
renderPricePanel(activeSeason);
}
function renderPricePanel(seasonKey) {
const state = priceState[seasonKey];
const modeToggleHtml = `
<div class="d-flex align-items-center gap-2 mb-2">
<div class="time-mode-toggle flex-grow-1">
<div class="time-mode-btn ${state.mode==='basic'?'active':''}" data-mode="basic">기본 시간대</div>
<div class="time-mode-btn ${state.mode==='detail'?'active':''}" data-mode="detail">세부 시간대</div>
</div>
</div>
`;
let bodyHtml;
if (state.mode === 'basic') {
bodyHtml = `<div id="i_basicPanel">` + BASIC_SLOTS.map(slot => `
<div class="basic-price-row">
<span class="basic-price-label">${slot.label}</span>
<div><input type="number" class="form-control form-control-sm basic-price-input i_basic_input" data-id="${slot.id}" value="${state.basic[slot.id]}"> 원/kWh</div>
</div>
`).join('') + `</div>`;
} else {
let rows = '';
for (let h = 0; h < 24; h++) {
const start = String(h).padStart(2,'0') + ':00';
const end = h === 23 ? '23:59' : String(h+1).padStart(2,'0') + ':00';
rows += `
<div class="detail-price-row">
<span class="detail-price-label">${String(h).padStart(2,'0')}시 (${start} - ${end})</span>
<div><input type="number" class="form-control form-control-sm detail-price-input i_detail_input" data-hour="${h}" value="${state.detail[h]}"> 원/kWh</div>
</div>`;
}
bodyHtml = `<div class="detail-scroll" id="i_detailPanel">${rows}</div>`;
}
$('#i_pricePanelsArea').html(modeToggleHtml + bodyHtml);
$('.time-mode-btn').off('click').on('click', function() {
saveCurrentPanelValues(seasonKey);
priceState[seasonKey].mode = $(this).data('mode');
renderPricePanel(seasonKey);
});
}
function saveCurrentPanelValues(seasonKey) {
const state = priceState[seasonKey];
if (state.mode === 'basic') {
$('.i_basic_input').each(function() { state.basic[$(this).data('id')] = parseFloat($(this).val()) || 0; });
} else {
$('.i_detail_input').each(function() { state.detail[$(this).data('hour')] = parseFloat($(this).val()) || 0; });
}
}
function loadChargerList() {
$.ajax({
url: "/cms/chgr/list.json", type: "POST", data: { start: 0, length: 9999 },
success: function(res) { renderChargerList(res.data || res.list || []); },
error: function() { $('#i_chargerListArea').html('<div class="text-muted small p-2">충전기 목록을 불러오지 못했습니다.</div>'); }
});
}
function renderChargerList(list) {
const mapped = list.map(c => ({
chargerCd: `${c.providerId || ''}${c.stId || ''}${c.chgrId || ''}`,
chargerName: c.chgrNm || `${c.providerId || ''}${c.stId || ''}${c.chgrId || ''}`,
location: c.stNm || '',
chargerType: c.speedTp || ''
}));
$('#i_chargerAllCount').text(mapped.length);
let html = mapped.map(c => `
<div class="charger-check-item" data-name="${c.chargerName.toLowerCase()}" data-loc="${c.location.toLowerCase()}" data-type="${c.chargerType}">
<input type="checkbox" class="charger-check-cb" value="${c.chargerCd}">
<label>
<div><strong>${c.chargerName}</strong> <span class="badge-square">${c.chargerType}</span></div>
<div class="text-muted" style="font-size:0.75rem;">${c.chargerCd} · ${c.location}</div>
</label>
</div>
`).join('');
$('#i_chargerListArea').html(html || '<div class="text-muted small p-2">등록된 충전기가 없습니다.</div>');
$('.charger-check-cb').off('change').on('change', updateAllCheckState);
}
function filterChargerList() {
const kw = ($('#i_chargerSearch').val() || '').toLowerCase();
const type = $('#i_chargerFilter').val();
$('.charger-check-item').each(function() {
const nameMatch = $(this).data('name').includes(kw) || $(this).data('loc').includes(kw);
const typeMatch = !type || $(this).data('type') === type;
$(this).toggle(nameMatch && typeMatch);
});
}
function updateAllCheckState() {
const total = $('.charger-check-cb:visible').length;
const checked = $('.charger-check-cb:visible:checked').length;
$('#i_chargerAll').prop('checked', total > 0 && total === checked);
}
function renderViewPriceTable(detailsTime) {
const seasonsSet = new Set(detailsTime.map(d => d.seasonType));
const seasons = seasonsSet.size ? Array.from(seasonsSet) : ['all_year'];
let priceMap = {};
let allIds = new Set();
detailsTime.forEach(d => {
(d.timeRates || []).forEach(r => {
priceMap[r.id] = priceMap[r.id] || {};
priceMap[r.id][d.seasonType] = r.price;
allIds.add(r.id);
});
});
const isBasic = [1, 2, 3].some(id => allIds.has(id));
let headerHtml = seasons.map(s => `<th>${SEASON_LABEL[s] || s}</th>`).join('');
let rows = '';
if (isBasic) {
BASIC_SLOTS.forEach(slot => {
rows += `<tr><td class="border-end py-2 fw-bold">${slot.label}</td>` +
seasons.map(s => `<td>${priceMap[slot.id] && priceMap[slot.id][s] != null ? `<span class="val-price">${priceMap[slot.id][s]}</span> <span class="val-unit">원/kWh</span>` : '-'}</td>`).join('') + `</tr>`;
});
} else {
for (let h = 0; h < 24; h++) {
const id = h + 4;
rows += `<tr><td class="border-end py-2">${String(h).padStart(2, '0')}시</td>` +
seasons.map(s => `<td>${priceMap[id] && priceMap[id][s] != null ? `<span class="val-price">${priceMap[id][s]}</span> <span class="val-unit">원/kWh</span>` : '-'}</td>`).join('') + `</tr>`;
}
}
$('#v_priceTableArea').html(`
<table class="table mb-0 text-center align-middle price-table">
<thead><tr><th class="border-end text-dark">시간대구분</th>${headerHtml}</tr></thead>
<tbody>${rows}</tbody>
</table>
`);
}
function submitRegisterForm() {
const seasons = getSeasons();
saveCurrentPanelValues(activeSeason);
let detailsTime = seasons.map(seasonKey => {
const state = priceState[seasonKey];
let timeRates = [];
if (state.mode === 'basic') {
BASIC_SLOTS.forEach(slot => timeRates.push({ id: slot.id, price: state.basic[slot.id] || 0 }));
} else {
for (let h = 0; h < 24; h++) timeRates.push({ id: h + 4, price: state.detail[h] || 0 });
}
return { seasonType: seasonKey, timeRates };
});
let chargerList = [];
$('.charger-check-cb:checked').each(function() {
chargerList.push({
chargerCd: $(this).val(),
applyStartDate: $('#i_applyStartDate').val(),
applyEndDate: $('#i_applyEndDate').val(),
isActive: true
});
});
let requestDto = {
ratePlanName: $('#i_ratePlanName').val(),
ratePlanType: "time",
seasonType: $('#i_seasonType').val(),
applyStartDate: $('#i_applyStartDate').val(),
applyEndDate: $('#i_applyEndDate').val(),
description: $('#i_description').val(),
isActive: true,
approvalStatus: "pending",
detailsTime: detailsTime,
chargerList: chargerList
};
if(!requestDto.ratePlanName) { alert("요금제명을 입력해주세요."); return; }
if(!requestDto.applyStartDate) { alert("적용 시작일을 입력해주세요."); return; }
if(!requestDto.applyEndDate) { alert("적용 종료일을 입력해주세요."); return; }
if(chargerList.length === 0) { alert("적용할 충전기를 1개 이상 선택해주세요."); return; }
$.ajax({
url: rootPath + "insert.json", type: "POST", contentType: "application/json; charset=utf-8", data: JSON.stringify(requestDto),
success: function(res) {
if(res.result === 'success') { alert("등록되었습니다."); } else { alert("오류 발생: " + res.message); }
$('.modal-footer .btn-cls-main').trigger('click');
datatable.ajax.reload();
},
error: function(err) {
alert("서버 통신 오류가 발생했습니다.");
$('.modal-footer .btn-cls-main').trigger('click');
}
});
}
$("#searchBtn").click(function() { datatable.ajax.reload(); });
$("#resetBtn").click(function() {
$("#ratePlanNameLike").val("");
$("#approvalStatusLike").val("");
$("#isActiveLike").val("");
datatable.ajax.reload();
});
</script>
</section>
</body>
</html>
@@ -6,12 +6,12 @@
<div th:replace="~{common/fragments/searchBox :: SearchBoxFragment(~{ :: #searchBoxContent}, ~{ :: #searchBoxFooter})}"> <div th:replace="~{common/fragments/searchBox :: SearchBoxFragment(~{ :: #searchBoxContent}, ~{ :: #searchBoxFooter})}">
<div id="searchBoxContent"> <div id="searchBoxContent">
<div class="row px-2"> <div class="row px-2">
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1"> <!-- <div class="col-lg-4 col-sm-6 col-xs-12 pb-1">-->
<div class="input-group"> <!-- <div class="input-group">-->
<span class="input-group-text" style="width:300px">요청 일시</span> <!-- <span class="input-group-text" style="width:300px">요청 일시</span>-->
<input type="text" id="searchDtLike" name="searchDtLike" class="form-control" placeholder="요청 일시 입력" maxlength="50"> <!-- <input type="text" id="searchDtLike" name="searchDtLike" class="form-control" placeholder="요청 일시 입력" maxlength="50">-->
</div> <!-- </div>-->
</div> <!-- </div>-->
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1"> <div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group"> <div class="input-group">
<span class="input-group-text" style="width:300px">충전사업자 ID</span> <span class="input-group-text" style="width:300px">충전사업자 ID</span>
@@ -30,12 +30,12 @@
<input type="text" id="chgrIdLike" name="chgrIdLike" class="form-control" placeholder="충전기 ID 입력" maxlength="50"> <input type="text" id="chgrIdLike" name="chgrIdLike" class="form-control" placeholder="충전기 ID 입력" maxlength="50">
</div> </div>
</div> </div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1"> <!-- <div class="col-lg-4 col-sm-6 col-xs-12 pb-1">-->
<div class="input-group"> <!-- <div class="input-group">-->
<span class="input-group-text" style="width:300px">플러그 타입</span> <!-- <span class="input-group-text" style="width:300px">플러그 타입</span>-->
<input type="text" id="plugTypeLike" name="plugTypeLike" class="form-control" placeholder="플러그 타입 입력" maxlength="50"> <!-- <input type="text" id="plugTypeLike" name="plugTypeLike" class="form-control" placeholder="플러그 타입 입력" maxlength="50">-->
</div> <!-- </div>-->
</div> <!-- </div>-->
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1"> <div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group"> <div class="input-group">
<span class="input-group-text" style="width:300px">회원카드번호</span> <span class="input-group-text" style="width:300px">회원카드번호</span>
@@ -44,34 +44,39 @@
</div> </div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1"> <div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group"> <div class="input-group">
<span class="input-group-text" style="width:300px">선결제 관련 신용카드 승인번호</span> <span class="input-group-text" style="width:300px">신용카드 승인번호</span>
<input type="text" id="creditPPayTrxNoLike" name="creditPPayTrxNoLike" class="form-control" placeholder="선결제 관련 신용카드 승인번호 입력" maxlength="50"> <input type="text" id="creditPPayTrxNoLike" name="creditPPayTrxNoLike" class="form-control" placeholder="신용카드 승인번호 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:300px">선결제 관련 신용카드 승인일시</span>
<input type="text" id="creditPPayTrxDtLike" name="creditPPayTrxDtLike" class="form-control" placeholder="선결제 관련 신용카드 승인일시 입력" maxlength="50">
</div> </div>
</div> </div>
<!-- <div class="col-lg-4 col-sm-6 col-xs-12 pb-1">-->
<!-- <div class="input-group">-->
<!-- <span class="input-group-text" style="width:300px">신용카드 승인일시</span>-->
<!-- <input type="text" id="creditPPayTrxDtLike" name="creditPPayTrxDtLike" class="form-control" placeholder="신용카드 승인일시 입력" maxlength="50">-->
<!-- </div>-->
<!-- </div>-->
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1"> <div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group"> <div class="input-group">
<span class="input-group-text" style="width:300px">충전 시작일시</span> <span class="input-group-text" style="width:300px">충전 시작일시</span>
<input type="text" id="rechgSdtLike" name="rechgSdtLike" class="form-control" placeholder="충전 시작일시 입력" maxlength="50"> <input type="date" id="rechgSdtLike" name="rechgSdtLike" class="form-control" placeholder="충전 시작일시 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:300px">충전 잔여 시간</span>
<input type="text" id="rechgRemainTimeLike" name="rechgRemainTimeLike" class="form-control" placeholder="충전 잔여 시간 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:300px">결제타입</span>
<input type="text" id="payTypeLike" name="payTypeLike" class="form-control" placeholder="결제타입 입력" maxlength="50">
</div> </div>
</div> </div>
<!-- <div class="col-lg-4 col-sm-6 col-xs-12 pb-1">-->
<!-- <div class="input-group">-->
<!-- <span class="input-group-text" style="width:300px">충전 잔여 시간</span>-->
<!-- <input type="text" id="rechgRemainTimeLike" name="rechgRemainTimeLike" class="form-control" placeholder="충전 잔여 시간 입력" maxlength="50">-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="col-lg-4 col-sm-6 col-xs-12 pb-1">-->
<!-- <div class="input-group">-->
<!-- <span class="input-group-text" style="width:300px">결제타입</span>-->
<!-- <select id="payType" name="payType" class="form-select">-->
<!-- <option value="">전체</option>-->
<!-- <option value="회원카드">회원카드</option>-->
<!-- <option value="신용카드">신용카드</option>-->
<!-- <option value="무과금">무과금</option>-->
<!-- </select>-->
<!-- </div>-->
<!-- </div>-->
</div> </div>
</div> </div>
<div id="searchBoxFooter"> <div id="searchBoxFooter">
@@ -123,11 +128,11 @@
order: [[0, 'desc'], [2, 'desc'], [3, 'desc'], [4, 'desc']], order: [[0, 'desc'], [2, 'desc'], [3, 'desc'], [4, 'desc']],
columns: [ columns: [
{title: "충전중 ID", name: "RECHGING_LIST_ID", data: "rechgingListId", className, visible: false}, {title: "충전중 ID", name: "RECHGING_LIST_ID", data: "rechgingListId", className, visible: false},
{title: "요청 일시", name: "SEARCH_DT", data: "searchDt", className, orderable: false}, // {title: "요청 일시", name: "SEARCH_DT", data: "searchDt", className, orderable: false},
{title: "충전사업자 ID", name: "PROVIDER_ID", data: "providerId", className}, {title: "충전사업자 ID", name: "PROVIDER_ID", data: "providerId", className},
{title: "충전소 ID", name: "ST_ID", data: "stId", className}, {title: "충전소 ID", name: "ST_ID", data: "stId", className},
{title: "충전기 ID", name: "CHGR_ID", data: "chgrId", className}, {title: "충전기 ID", name: "CHGR_ID", data: "chgrId", className},
{title: "채널 ID", name: "CH_ID", data: "chId", className, orderable: false}, // {title: "채널 ID", name: "CH_ID", data: "chId", className, orderable: false},
{title: "플러그 타입", name: "PLUG_TYPE", data: "plugType", className, orderable: false}, {title: "플러그 타입", name: "PLUG_TYPE", data: "plugType", className, orderable: false},
{title: "회원카드번호", name: "MEM_AUTH_INPUT_NO", data: "memAuthInputNo", className, orderable: false}, {title: "회원카드번호", name: "MEM_AUTH_INPUT_NO", data: "memAuthInputNo", className, orderable: false},
{title: "신용카드 승인번호", name: "CREDIT_P_PAY_TRX_NO", data: "creditPPayTrxNo", className, orderable: false}, {title: "신용카드 승인번호", name: "CREDIT_P_PAY_TRX_NO", data: "creditPPayTrxNo", className, orderable: false},
@@ -138,21 +143,9 @@
{title: "선결제 금액", name: "RECHG_DEMAND_AMT", data: "rechgDemandAmt", className, orderable: false}, {title: "선결제 금액", name: "RECHG_DEMAND_AMT", data: "rechgDemandAmt", className, orderable: false},
{title: "진행 충전 금액", name: "RECHGING_AMT", data: "rechgingAmt", className, orderable: false}, {title: "진행 충전 금액", name: "RECHGING_AMT", data: "rechgingAmt", className, orderable: false},
{title: "결제타입", name: "PAY_TYPE", data: "payType", className, orderable: false}, {title: "결제타입", name: "PAY_TYPE", data: "payType", className, orderable: false},
{title: "적산량", name: "INTEGRATED_WH", data: "integratedWh", className, orderable: false}, // {title: "기능", orderable:false, width:"110px",
{title: "DC 적산량", name: "DC_POWER_WH", data: "dcPowerWh", className, orderable: false}, // render: (data, type, row) => {
{title: "AC 적산량", name: "AC_POWER_WH", data: "acPowerWh", className, orderable: false}, // return mkRowDataFunctions({rechgingListId: row['rechgingListId'], providerId: row['providerId'], stId: row['stId'], chgrId: row['chgrId']}, row['#####Title'], true, menuRoleModify, menuRoleDel);}}
{title: "현재 전압", name: "CURR_VOLT", data: "currVolt", className, orderable: false},
{title: "현재 전류", name: "CURR_C", data: "currC", className, orderable: false},
{title: "등록인ID", name: "REG_ID", data: "regIdNm", className, orderable: false},
{title: "등록일시", name: "REG_DT", data: "regDt", className, orderable: false,
render: (data, type, row) => dateUtil.toYmd(row["regDt"]) },
{title: "수정인ID", name: "UPD_ID", data: "updIdNm", className, orderable: false},
{title: "수정일시", name: "UPD_DT", data: "updDt", className, orderable: false,
render: (data, type, row) => dateUtil.toYmd(row["updDt"]) },
{title: "삭제인ID", name: "DEL_ID", data: "delIdNm", className, orderable: false},
{title: "삭제일시", name: "DEL_DT", data: "delDt", className, orderable: false,
render: (data, type, row) => dateUtil.toYmd(row["delDt"]) },
{title: "사용상태", name: "REC_ST", data: "recSt", className, orderable: false}
], ],
allCheck: false, allCheck: false,
}); });
@@ -247,22 +240,22 @@
maxLen: 20, maxLen: 20,
}), }),
new ModalInput({ new ModalInput({
inputType: ModalInputType.text, inputType: ModalInputType.textarea,
inputId: "creditPPayTrxNo", inputId: "creditPPayTrxNo",
inputName: "creditPPayTrxNo", inputName: "creditPPayTrxNo",
inputLabel: "선결제 관련 신용카드 승인번호", inputLabel: "신용카드 승인번호",
inputPlaceholder: "선결제 관련 신용카드 승인번호", inputPlaceholder: "신용카드 승인번호",
isReq: false, isReq: false,
isEnable: true, isEnable: true,
minLen: 0, minLen: 0,
maxLen: 20, maxLen: 250,
}), }),
new ModalInput({ new ModalInput({
inputType: ModalInputType.textarea, inputType: ModalInputType.textarea,
inputId: "creditPPayTrxDt", inputId: "creditPPayTrxDt",
inputName: "creditPPayTrxDt", inputName: "creditPPayTrxDt",
inputLabel: "선결제 관련 신용카드 승인일시", inputLabel: "신용카드 승인일시",
inputPlaceholder: "선결제 관련 신용카드 승인일시", inputPlaceholder: "신용카드 승인일시",
isReq: false, isReq: false,
isEnable: true, isEnable: true,
minLen: 0, minLen: 0,
@@ -368,8 +361,8 @@
inputType: ModalInputType.text, inputType: ModalInputType.text,
inputId: "currC", inputId: "currC",
inputName: "currC", inputName: "currC",
inputLabel: "현재 류", inputLabel: "현재 류",
inputPlaceholder: "현재 류", inputPlaceholder: "현재 류",
isReq: false, isReq: false,
isEnable: true, isEnable: true,
}) })
@@ -380,7 +373,7 @@
const loadUrl = rootPath+"view.json"; const loadUrl = rootPath+"view.json";
const deleteUrl = rootPath+"delete.json"; const deleteUrl = rootPath+"delete.json";
const modalWidth = 500; const modalWidth = 500;
const labelWidth = '90px'; const labelWidth = '100px';
if(mode === ModalMode.REGISTER) { if(mode === ModalMode.REGISTER) {
newModal(new ModalInfo({modalTitle: "등록", inputArray: modalInputArray, modalWidth}), mode, 'main', {insertUrl, labelWidth, callBack: () => { defaultCallback(); }}) newModal(new ModalInfo({modalTitle: "등록", inputArray: modalInputArray, modalWidth}), mode, 'main', {insertUrl, labelWidth, callBack: () => { defaultCallback(); }})
} else if(mode===ModalMode.VIEW || mode===ModalMode.VIEW_ONLY || mode===ModalMode.MODIFY) { } else if(mode===ModalMode.VIEW || mode===ModalMode.VIEW_ONLY || mode===ModalMode.MODIFY) {
@@ -18,24 +18,6 @@
<input type="text" id="addrLdMLike" name="addrLdMLike" class="form-control" placeholder="주소 지번 메인 입력" maxlength="50"> <input type="text" id="addrLdMLike" name="addrLdMLike" class="form-control" placeholder="주소 지번 메인 입력" maxlength="50">
</div> </div>
</div> </div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:200px">법정동 도 명</span>
<input type="text" id="pnuDoNmLike" name="pnuDoNmLike" class="form-control" placeholder="법정동 도 명 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:200px">법정동 시 명</span>
<input type="text" id="pnuSiNmLike" name="pnuSiNmLike" class="form-control" placeholder="법정동 시 명 입력" maxlength="50">
</div>
</div>
<div class="col-lg-4 col-sm-6 col-xs-12 pb-1">
<div class="input-group">
<span class="input-group-text" style="width:200px">법정동 읍면동 명</span>
<input type="text" id="pnuDongNmLike" name="pnuDongNmLike" class="form-control" placeholder="법정동 읍면동 명 입력" maxlength="50">
</div>
</div>
</div> </div>
</div> </div>
<div id="searchBoxFooter"> <div id="searchBoxFooter">