Compare commits
23
Commits
b85dd70fd6
..
jaeeun
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ac3011085 | ||
|
|
055a2bed6d | ||
|
|
fa473e8728 | ||
|
|
eb9b91cb7c | ||
|
|
1de5589ef2 | ||
|
|
2e56108a3b | ||
|
|
cac5f79a40 | ||
|
|
38102a8d34 | ||
|
|
b035537430 | ||
|
|
8a6ebcd8eb | ||
|
|
5feb7845fd | ||
|
|
992f842be8 | ||
|
|
4970d3d5fa | ||
|
|
bcfdfe52a9 | ||
|
|
14f96f037c | ||
|
|
e047865657 | ||
|
|
290928a839 | ||
|
|
938fae0359 | ||
|
|
852d48c1b7 | ||
|
|
318a741c19 | ||
|
|
69a0529b7c | ||
|
|
802b3e3224 | ||
|
|
8d29a742c2 |
Binary file not shown.
Binary file not shown.
@@ -40,10 +40,4 @@ public class MainController {
|
||||
return HttpUtil.responseList(bodArtiService.selectList(srch));
|
||||
}
|
||||
|
||||
@GetMapping("/cms/test")
|
||||
@ResponseBody
|
||||
public ResponseEntity<String> test() {
|
||||
List<Station> stations = stationService.getStations();
|
||||
return ResponseEntity.ok("테스트 ㅇ! 건수: " + stations.size());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -1,33 +1,101 @@
|
||||
package net.jwsi.jcms.vpp.api;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.jwsi.jcms.vpp.chgr.ChgrResponseDto;
|
||||
import net.jwsi.jcms.vpp.chgrCurrState.ChgrCurrState;
|
||||
import net.jwsi.jcms.vpp.chgrHist.ChgrHistResponseDto;
|
||||
import net.jwsi.jcms.vpp.provider.ProviderDto;
|
||||
import net.jwsi.jcms.vpp.rechgingList.RechgingListDto;
|
||||
import net.jwsi.jcms.vpp.ratePlan.RatePlanResponseDto;
|
||||
import net.jwsi.jcms.vpp.station.StationDto;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestClient;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ApiClient {
|
||||
|
||||
private final RestClient restClient;
|
||||
private final RestClient externalApiClient;
|
||||
|
||||
public ApiResponse<List<StationDto>> fetchStations(String baseUrl, String token) {
|
||||
URI uri = UriComponentsBuilder.fromUriString(baseUrl)
|
||||
.path("/api/st/list")
|
||||
.build()
|
||||
.toUri();
|
||||
|
||||
return restClient.post()
|
||||
.uri(uri)
|
||||
.header("Authorization", "Bearer " + token)
|
||||
public ApiResponse<List<StationDto>> fetchStations() {
|
||||
return externalApiClient.post()
|
||||
.uri("/api/st/list")
|
||||
.body(Collections.emptyMap())
|
||||
.retrieve()
|
||||
.body(new ParameterizedTypeReference<ApiResponse<List<StationDto>>>() {});
|
||||
}
|
||||
|
||||
public ApiResponse<List<ProviderDto>> fetchProviders() {
|
||||
return externalApiClient.get()
|
||||
.uri("/api/provider/list")
|
||||
.retrieve()
|
||||
.body(new ParameterizedTypeReference<ApiResponse<List<ProviderDto>>>() {});
|
||||
}
|
||||
|
||||
public ApiResponse<List<ChgrResponseDto>> fetchChgr() {
|
||||
return externalApiClient.post()
|
||||
.uri("/api/charger/list")
|
||||
.body(Collections.emptyMap())
|
||||
.retrieve()
|
||||
.body(new ParameterizedTypeReference<ApiResponse<List<ChgrResponseDto>>>() {});
|
||||
}
|
||||
|
||||
public ApiResponse<List<ChgrHistResponseDto>> fetchChgrHist(String startDt, String pageSize) {
|
||||
Map<String, String> requestBody = Map.of(
|
||||
"startDt", startDt,
|
||||
"pageSize", pageSize
|
||||
);
|
||||
|
||||
return externalApiClient.post()
|
||||
.uri("/api/charge/hist")
|
||||
.body(requestBody)
|
||||
.retrieve()
|
||||
.body(new ParameterizedTypeReference<ApiResponse<List<ChgrHistResponseDto>>>() {});
|
||||
}
|
||||
|
||||
public ApiResponse<List<ChgrCurrState>> fetchChgrCurrState(String pageSize) {
|
||||
Map<String, String> requestBody = Map.of(
|
||||
"pageSize", pageSize
|
||||
);
|
||||
|
||||
return externalApiClient.post()
|
||||
.uri("/api/charge/stateInfo")
|
||||
.body(requestBody)
|
||||
.retrieve()
|
||||
.body(new ParameterizedTypeReference<ApiResponse<List<ChgrCurrState>>>() {});
|
||||
}
|
||||
|
||||
public ApiResponse<List<RechgingListDto>> fetchRechgingList() {
|
||||
|
||||
return externalApiClient.post()
|
||||
.uri("/api/charge/chargingInfo")
|
||||
.body(Collections.emptyMap())
|
||||
.retrieve()
|
||||
.body(new ParameterizedTypeReference<ApiResponse<List<RechgingListDto>>>() {});
|
||||
}
|
||||
|
||||
|
||||
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,13 @@
|
||||
package net.jwsi.jcms.vpp.api;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
public class ApiToken {
|
||||
private String jwt;
|
||||
private List<String> roles;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package net.jwsi.jcms.vpp.api;
|
||||
|
||||
import net.jwsi.jcms.vpp.utils.JwtUtil;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestClient;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class ExternalApiService {
|
||||
|
||||
private final RestClient authRestClient = RestClient.create();
|
||||
|
||||
@Value("${jcms.api.base-url}")
|
||||
private String baseUrl;
|
||||
|
||||
@Value("${jcms.api.user-id}")
|
||||
private String
|
||||
apiUserId;
|
||||
|
||||
@Value("${jcms.api.user-pwd}")
|
||||
private String apiUserPwd;
|
||||
|
||||
private String cachedToken;
|
||||
private LocalDateTime tokenExpiryTime;
|
||||
|
||||
public synchronized String getAccessToken() {
|
||||
if (cachedToken != null && tokenExpiryTime != null && LocalDateTime.now().plusMinutes(1).isBefore(tokenExpiryTime)) {
|
||||
return cachedToken;
|
||||
}
|
||||
|
||||
URI uri = UriComponentsBuilder.fromUriString(baseUrl)
|
||||
.path("/api/auth/token")
|
||||
.build()
|
||||
.toUri();
|
||||
|
||||
Map<String, String> requestBody = Map.of(
|
||||
"userId", apiUserId,
|
||||
"pwd", apiUserPwd
|
||||
);
|
||||
|
||||
ApiResponse<ApiToken> response = authRestClient.post()
|
||||
.uri(uri)
|
||||
.body(requestBody)
|
||||
.retrieve()
|
||||
.body(new ParameterizedTypeReference<ApiResponse<ApiToken>>() {});
|
||||
|
||||
if (response != null && response.getData() != null) {
|
||||
ApiToken tokenDto = response.getData();
|
||||
this.cachedToken = tokenDto.getJwt();
|
||||
this.tokenExpiryTime = JwtUtil.getExpirationTime(this.cachedToken);
|
||||
|
||||
return this.cachedToken;
|
||||
}
|
||||
|
||||
throw new IllegalStateException("외부 API 토큰 발급에 실패했습니다.");
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,25 @@
|
||||
package net.jwsi.jcms.vpp.api;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.web.client.RestClient;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@Configuration
|
||||
public class RestClientConfig {
|
||||
|
||||
@Bean
|
||||
public RestClient restClient() {
|
||||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
|
||||
factory.setConnectTimeout((int) Duration.ofSeconds(5).toMillis());
|
||||
factory.setReadTimeout((int) Duration.ofSeconds(10).toMillis());
|
||||
@Value("${jcms.api.base-url}")
|
||||
private String baseUrl;
|
||||
|
||||
@Bean
|
||||
public RestClient externalApiClient(ExternalApiService externalApiService) {
|
||||
return RestClient.builder()
|
||||
.requestFactory(factory)
|
||||
.baseUrl(baseUrl)
|
||||
.requestInterceptor((request, body, execution) -> {
|
||||
String token = externalApiService.getAccessToken();
|
||||
request.getHeaders().set("Authorization", "Bearer " + token);
|
||||
return execution.execute(request, body);
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package net.jwsi.jcms.vpp.chgr;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import net.jwsi.jcms.base.BaseStEntity;
|
||||
|
||||
@NoArgsConstructor
|
||||
@Setter
|
||||
@Getter
|
||||
@ToString
|
||||
@Entity(name = "vpp_chgr")
|
||||
@IdClass(ChgrId.class)
|
||||
public class Chgr extends BaseStEntity {
|
||||
|
||||
@Transient
|
||||
private ChgrId oldId__;
|
||||
|
||||
@Id
|
||||
@Column(name = "CHGR_ID")
|
||||
private String chgrId; //충전기 id(pk)
|
||||
@Id
|
||||
@Column(name = "PROVIDER_ID")
|
||||
private String providerId; //충전사업자 id(pk) @Id
|
||||
@Id
|
||||
@Column(name = "ST_ID")
|
||||
private String stId; //충전소 id(pk) @Id
|
||||
private String stNm; //충전소 명
|
||||
private String chgrNm; //충전기 명
|
||||
private String speedTp; //충전기속도 코드
|
||||
private String chgrTp; //충전기타입 코드
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package net.jwsi.jcms.vpp.chgr;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.jwsi.jcms.base.BaseController;
|
||||
import net.jwsi.jcms.exception.JsonDataException;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Controller
|
||||
@Slf4j
|
||||
@RequestMapping("/cms/chgr/")
|
||||
public class ChgrController extends BaseController<Chgr,String,ChgrRepository,ChgrService> {
|
||||
|
||||
private final String path = "/system/chgr/";
|
||||
|
||||
private final ChgrService chgrService;
|
||||
|
||||
|
||||
public ChgrController(ChgrService service, ChgrService chgrService) {
|
||||
super(service);
|
||||
this.chgrService = chgrService;
|
||||
}
|
||||
@GetMapping("list")
|
||||
public String list(){
|
||||
chgrService.getCharger();
|
||||
return path+"list";
|
||||
}
|
||||
@PostMapping("list.json")
|
||||
@ResponseBody
|
||||
public Map<String,Object> list(Chgr chgr){
|
||||
return svcSelectList(chgr);
|
||||
}
|
||||
@PostMapping("view.json")
|
||||
@ResponseBody
|
||||
public Map<String, Object> viewData(Chgr chgr) throws JsonDataException {
|
||||
return svcSelectOne(chgr);
|
||||
}
|
||||
|
||||
@PostMapping("insert.json")
|
||||
@ResponseBody
|
||||
public Map<String, Object> insert(Chgr chgr) throws JsonDataException {
|
||||
return svcInsert(chgr);
|
||||
}
|
||||
|
||||
@PostMapping("update.json")
|
||||
@ResponseBody
|
||||
public Map<String, Object> update(Chgr chgr) throws JsonDataException {
|
||||
return svcUpdate(chgr);
|
||||
}
|
||||
|
||||
@PostMapping("delete.json")
|
||||
@ResponseBody
|
||||
public Map<String, Object> delete(Chgr chgr) throws JsonDataException {
|
||||
return svcDelete(chgr.getChgrId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void chkSelectList(List list) throws JsonDataException {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void chkSelect(Chgr data) throws JsonDataException {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package net.jwsi.jcms.vpp.chgr;
|
||||
|
||||
import net.jwsi.jcms.base.BaseRecStRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface ChgrRepository extends BaseRecStRepository<Chgr,String> {
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package net.jwsi.jcms.vpp.chgr;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class ChgrResponseDto {
|
||||
private String providerId;
|
||||
private String stId;
|
||||
private String stNm;
|
||||
private String chgrId;
|
||||
private String chgrNm;
|
||||
private String speedTp;
|
||||
private String chgrType;
|
||||
private double gpsXpos;
|
||||
private double gpsYpos;
|
||||
private String locInfo;
|
||||
private String memshOnly;
|
||||
private String installYy;
|
||||
private String simulRechg;
|
||||
private String simulChgrId;
|
||||
|
||||
public Chgr toEntity(){
|
||||
Chgr chgr = new Chgr();
|
||||
chgr.setChgrId(this.chgrId);
|
||||
chgr.setProviderId(this.providerId);
|
||||
chgr.setStId(this.stId);
|
||||
chgr.setStNm(this.stNm);
|
||||
chgr.setChgrNm(this.chgrNm);
|
||||
chgr.setSpeedTp(this.speedTp);
|
||||
chgr.setChgrTp(this.chgrType);
|
||||
return chgr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package net.jwsi.jcms.vpp.chgr;
|
||||
|
||||
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.api.ApiClient;
|
||||
import net.jwsi.jcms.vpp.api.ApiResponse;
|
||||
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.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class ChgrService extends BaseService<Chgr,String,ChgrRepository> {
|
||||
|
||||
private final ApiClient apiClient;
|
||||
public ChgrService(ChgrRepository repository, ApiClient apiClient) {
|
||||
super(repository);
|
||||
this.apiClient = apiClient;
|
||||
}
|
||||
@Transactional
|
||||
@Scheduled(cron = "0 0 0 * * *")
|
||||
public void getCharger() {
|
||||
ApiResponse<List<ChgrResponseDto>> response = apiClient.fetchChgr();
|
||||
|
||||
if (response == null || !Integer.valueOf(200).equals(response.getCode())) {
|
||||
String msg = (response != null) ? response.getMessage() : "응답 없음";
|
||||
throw new RuntimeException("API 호출 실패: " + msg);
|
||||
}
|
||||
|
||||
List<ChgrResponseDto> dtoList = response.getData();
|
||||
if (dtoList == null || dtoList.isEmpty()) {
|
||||
log.info("가져온 충전소 데이터가 없습니다.");
|
||||
}
|
||||
|
||||
List<Chgr> entityList = dtoList.stream()
|
||||
.map(ChgrResponseDto::toEntity)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Chgr> savedStations = super.repository.saveAll(entityList);
|
||||
|
||||
log.info("총 {}건의 충전소 데이터가 DB에 동기화되었습니다.", savedStations.size());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer getUserId() {
|
||||
return HttpUtil.getUserId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getUserName(Integer id) {
|
||||
return LoginUtil.getUserName(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Chgr newSearchParam(Chgr chgr) {
|
||||
if(chgr == null){ chgr = new Chgr();}
|
||||
return chgr;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnumSqlType useSqlTyp() {
|
||||
return EnumSqlType.SPECIFICATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Specification<Chgr> getSpecification(Chgr chgr) {
|
||||
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
|
||||
protected Page<Chgr> _selectPage(Chgr chgr) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Chgr> _selectList(Chgr chgr) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package net.jwsi.jcms.vpp.chgr;
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package net.jwsi.jcms.vpp.chgrCurrState;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import net.jwsi.jcms.base.BaseStEntity;
|
||||
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@Entity(name = "vpp_chgr_curr_state")
|
||||
@IdClass(ChgrCurrStateId.class)
|
||||
public class ChgrCurrState extends BaseStEntity {
|
||||
|
||||
@Transient
|
||||
private ChgrCurrStateId oldId__;
|
||||
|
||||
@Id
|
||||
@Column(name = "provider_id")
|
||||
private String providerId; // provider_id (PK)
|
||||
|
||||
@Id
|
||||
@Column(name = "st_id")
|
||||
private String stId; // st_id (PK)
|
||||
|
||||
@Id
|
||||
@Column(name = "chgr_id")
|
||||
private String chgrId; // chgr_id (PK)
|
||||
|
||||
@Column(name = "state_dt")
|
||||
private String stateDt;
|
||||
|
||||
@Column(name = "op_mode")
|
||||
private String opMode;
|
||||
|
||||
@Column(name = "ch1_rechg_state_cd")
|
||||
private String ch1RechgStateCd;
|
||||
|
||||
@Column(name = "ch1_door_state_cd")
|
||||
private String ch1DoorStateCd;
|
||||
|
||||
@Column(name = "ch1_plug_state_cd")
|
||||
private String ch1PlugStateCd;
|
||||
|
||||
@Column(name = "ch2_rechg_state_cd")
|
||||
private String ch2RechgStateCd;
|
||||
|
||||
@Column(name = "ch2_door_state_cd")
|
||||
private String ch2DoorStateCd;
|
||||
|
||||
@Column(name = "ch2_plug_state_cd")
|
||||
private String ch2PlugStateCd;
|
||||
|
||||
@Column(name = "ch3_rechg_state_cd")
|
||||
private String ch3RechgStateCd;
|
||||
|
||||
@Column(name = "ch3_door_state_cd")
|
||||
private String ch3DoorStateCd;
|
||||
|
||||
@Column(name = "ch3_plug_state_cd")
|
||||
private String ch3PlugStateCd;
|
||||
|
||||
@Column(name = "integrated_kwh")
|
||||
private Double integratedKwh;
|
||||
|
||||
@Column(name = "dc_power_wh")
|
||||
private Double dcPowerWh;
|
||||
|
||||
@Column(name = "ac_power_wh")
|
||||
private Double acPowerWh;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package net.jwsi.jcms.vpp.chgrCurrState;
|
||||
|
||||
import net.jwsi.jcms.base.BaseController;
|
||||
import net.jwsi.jcms.exception.JsonDataException;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/cms/chgrCurrState/")
|
||||
public class ChgrCurrStateController extends BaseController<ChgrCurrState,ChgrCurrStateId,ChgrCurrStateRepository,ChgrCurrStateService> {
|
||||
|
||||
private final ChgrCurrStateService chgrCurrStateService;
|
||||
private final String path = "/system/chgrCurrState/";
|
||||
|
||||
public ChgrCurrStateController(ChgrCurrStateService service, ChgrCurrStateService chgrCurrStateService) {
|
||||
super(service);
|
||||
this.chgrCurrStateService = chgrCurrStateService;
|
||||
}
|
||||
|
||||
@GetMapping("list")
|
||||
public String list(){
|
||||
chgrCurrStateService.getChgrCurrState();
|
||||
return path+"list";
|
||||
}
|
||||
@PostMapping("list.json")
|
||||
@ResponseBody
|
||||
public Map<String,Object> list(ChgrCurrState chgrCurrState){
|
||||
return svcSelectList(chgrCurrState);
|
||||
}
|
||||
@PostMapping("view.json")
|
||||
@ResponseBody
|
||||
public Map<String, Object> viewData(ChgrCurrState chgrCurrState) throws JsonDataException {
|
||||
return svcSelectOne(chgrCurrState);
|
||||
}
|
||||
|
||||
@PostMapping("insert.json")
|
||||
@ResponseBody
|
||||
public Map<String, Object> insert(ChgrCurrState chgrCurrState) throws JsonDataException {
|
||||
return svcInsert(chgrCurrState);
|
||||
}
|
||||
|
||||
@PostMapping("update.json")
|
||||
@ResponseBody
|
||||
public Map<String, Object> update(ChgrCurrState chgrCurrState) throws JsonDataException {
|
||||
return svcUpdate(chgrCurrState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void chkSelectList(List<ChgrCurrState> list) throws JsonDataException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void chkSelect(ChgrCurrState data) throws JsonDataException {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package net.jwsi.jcms.vpp.chgrCurrState;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode
|
||||
public class ChgrCurrStateId implements Serializable {
|
||||
|
||||
private String providerId;
|
||||
private String stId;
|
||||
private String chgrId;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package net.jwsi.jcms.vpp.chgrCurrState;
|
||||
|
||||
import net.jwsi.jcms.base.BaseRecStRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface ChgrCurrStateRepository extends BaseRecStRepository<ChgrCurrState,ChgrCurrStateId> {
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package net.jwsi.jcms.vpp.chgrCurrState;
|
||||
|
||||
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.api.ApiClient;
|
||||
import net.jwsi.jcms.vpp.api.ApiResponse;
|
||||
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.List;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class ChgrCurrStateService extends BaseService<ChgrCurrState,ChgrCurrStateId,ChgrCurrStateRepository> {
|
||||
|
||||
private final ApiClient apiClient;
|
||||
|
||||
public ChgrCurrStateService(ChgrCurrStateRepository repository, ApiClient apiClient) {
|
||||
super(repository);
|
||||
this.apiClient = apiClient;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Scheduled(cron = "0 0 0 * * *")
|
||||
public void getChgrCurrState() {
|
||||
ApiResponse<List<ChgrCurrState>> response = apiClient.fetchChgrCurrState("12");
|
||||
|
||||
if (response == null || !Integer.valueOf(200).equals(response.getCode())) {
|
||||
String msg = (response != null) ? response.getMessage() : "응답 없음";
|
||||
throw new RuntimeException("API 호출 실패: " + msg);
|
||||
}
|
||||
|
||||
List<ChgrCurrState> list = response.getData();
|
||||
if (list == null || list.isEmpty()) {
|
||||
log.info("가져온 충전소 데이터가 없습니다.");
|
||||
}
|
||||
|
||||
List<ChgrCurrState> savedStations = super.repository.saveAll(list);
|
||||
|
||||
log.info("총 {}건의 충전소 데이터가 DB에 동기화되었습니다.", savedStations.size());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer getUserId() {
|
||||
return HttpUtil.getUserId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getUserName(Integer id) {
|
||||
return LoginUtil.getUserName(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChgrCurrState newSearchParam(ChgrCurrState chgrCurrState) {
|
||||
if(chgrCurrState == null){
|
||||
chgrCurrState = new ChgrCurrState();
|
||||
}
|
||||
return chgrCurrState;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnumSqlType useSqlTyp() {
|
||||
return EnumSqlType.SPECIFICATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Specification<ChgrCurrState> getSpecification(ChgrCurrState chgrCurrState) {
|
||||
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
|
||||
protected Page<ChgrCurrState> _selectPage(ChgrCurrState chgrCurrState) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ChgrCurrState> _selectList(ChgrCurrState chgrCurrState) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package net.jwsi.jcms.vpp.chgrHist;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import net.jwsi.jcms.base.BaseStEntity;
|
||||
|
||||
@NoArgsConstructor
|
||||
@Setter
|
||||
@Getter
|
||||
@ToString
|
||||
@Entity(name="vpp_chgr_hist")
|
||||
public class ChgrHist extends BaseStEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "CHGR_HIST_ID")
|
||||
private Integer chgrHistId; // 충전이력 id
|
||||
|
||||
@Column(name = "CHGR_ID", nullable = false)
|
||||
private String chgrId; // 충전기 id
|
||||
|
||||
@Column(name = "CH_ID")
|
||||
private Integer chId;
|
||||
|
||||
@Column(name = "ST_ID", nullable = false)
|
||||
private String stId; // 충전소 id
|
||||
|
||||
@Column(name = "PAY_TP")
|
||||
private String payTp; // 결제구분
|
||||
|
||||
@Column(name = "PLUG_TP")
|
||||
private String plugTp; // 충전기타입 코드
|
||||
|
||||
@Column(name = "RECHG_S_DT")
|
||||
private String rechgSDt; // 충전 시작일시 (rechgsdt -> RECHG_S_DT 매핑)
|
||||
|
||||
@Column(name = "RECHG_E_DT")
|
||||
private String rechgEDt; // 충전 종료일시 (rechgedt -> RECHG_E_DT 매핑)
|
||||
|
||||
@Column(name = "RECHG_TIME")
|
||||
private String rechgTime; // 충전시간
|
||||
|
||||
@Column(name = "RECHG_WH")
|
||||
private Integer rechGwh; // 충전량
|
||||
|
||||
@Column(name = "RECHG_AMT")
|
||||
private Integer rechgAmt; // 충전금액
|
||||
|
||||
@Column(name = "PAY_FNSH")
|
||||
private String payFnsh; // 신용결제완료구분
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package net.jwsi.jcms.vpp.chgrHist;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.jwsi.jcms.base.BaseController;
|
||||
import net.jwsi.jcms.exception.JsonDataException;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/cms/chgrHist/")
|
||||
@Slf4j
|
||||
public class ChgrHistController extends BaseController<ChgrHist,Integer,ChgrHistRepository,ChgrHistService> {
|
||||
|
||||
private final String path = "/system/chgrHist/";
|
||||
private final ChgrHistService chgrHistService;
|
||||
public ChgrHistController(ChgrHistService service, ChgrHistService chgrHistService) {
|
||||
super(service);
|
||||
this.chgrHistService = chgrHistService;
|
||||
}
|
||||
|
||||
@GetMapping("list")
|
||||
public String list() throws IllegalAccessException {
|
||||
chgrHistService.getChgrHist();
|
||||
return path+"list";
|
||||
}
|
||||
|
||||
@PostMapping("list.json")
|
||||
@ResponseBody
|
||||
public Map<String,Object> list(ChgrHist chgrHist){
|
||||
return svcSelectList(chgrHist);
|
||||
}
|
||||
@PostMapping("insert.json")
|
||||
@ResponseBody
|
||||
public Map<String,Object> insert(ChgrHist chgrHist){
|
||||
return svcInsert(chgrHist);
|
||||
}
|
||||
@PostMapping("delete.json")
|
||||
@ResponseBody
|
||||
public Map<String,Object> delete(ChgrHist chgrHist){
|
||||
return svcDelete(chgrHist.getChgrHistId());
|
||||
}
|
||||
@PostMapping("update.json")
|
||||
@ResponseBody
|
||||
public Map<String,Object> update(ChgrHist chgrHist){
|
||||
return svcUpdate(chgrHist);
|
||||
}
|
||||
@PostMapping("view.json")
|
||||
@ResponseBody
|
||||
public Map<String,Object> view(ChgrHist chgrHist){
|
||||
return svcSelectOne(chgrHist);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void chkSelectList(List<ChgrHist> list) throws JsonDataException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void chkSelect(ChgrHist data) throws JsonDataException {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package net.jwsi.jcms.vpp.chgrHist;
|
||||
|
||||
import net.jwsi.jcms.base.BaseRecStRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface ChgrHistRepository extends BaseRecStRepository<ChgrHist,Integer> {
|
||||
boolean existsByChgrIdAndChIdAndRechgEDt(String chgrId, Integer chId,String rechgEDt);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package net.jwsi.jcms.vpp.chgrHist;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.*;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class ChgrHistResponseDto {
|
||||
|
||||
private String providerId; // 충전사업자 ID (Length: 2)
|
||||
private String stId; // 충전소 ID (Length: 6)
|
||||
private String chgrId; // 충전기 ID (Length: 2)
|
||||
private String speedTp; // 충전 속도 (급속, 완속)
|
||||
private Integer chId; // 채널 ID
|
||||
private String plugType; // 플러그 타입
|
||||
private String memAuthInputNo; // 회원카드번호 (Length: 16)
|
||||
|
||||
private String creditPPayTrxNo; // 선결제 관련 신용카드 승인번호 (Length: 20)
|
||||
private String creditPPayTrxDt; // 선결제 관련 신용카드 승인일시 (yyyy-MM-dd HH:mm:ss)
|
||||
|
||||
private String rechgSdt; // 충전 시작일시 (yyyy-MM-dd HH:mm:ss)
|
||||
private String rechgEdt; // 충전 종료일시 (yyyy-MM-dd HH:mm:ss)
|
||||
private String rechgTime; // 충전시간 (Length: 10)
|
||||
|
||||
private Integer rechgWh; // 충전량(wh)
|
||||
private Integer rechgDemandAmt; // 선결제 금액
|
||||
private Integer rechgAmt; // 충전 금액
|
||||
private Integer cancelAmt; // 취소 금액
|
||||
|
||||
private String payType; // 결제 타입 (회원카드, 신용카드, 무과금)
|
||||
private String rechgFnshTp; // 충전 완료 구분
|
||||
private String payFnsh; // 결제 완료 구분 (결제완료, 결제실패, 기타결제, 알수없음)
|
||||
@JsonProperty("IntegratedWh")
|
||||
private JsonNode integratedWh;
|
||||
private Double dcPowerWh; // DC 적산량(wh)
|
||||
private Double acPowerWh; // AC 적산량(wh)
|
||||
|
||||
|
||||
public ChgrHist toEntity() {
|
||||
ChgrHist chgrHist = new ChgrHist();
|
||||
chgrHist.setChgrId(this.chgrId);
|
||||
chgrHist.setStId(this.stId);
|
||||
chgrHist.setPayTp(this.payType);
|
||||
chgrHist.setPlugTp(this.plugType);
|
||||
chgrHist.setRechgSDt(this.rechgSdt);
|
||||
chgrHist.setRechgEDt(this.rechgEdt);
|
||||
chgrHist.setRechgTime(this.rechgTime);
|
||||
chgrHist.setRechgAmt(this.rechgAmt);
|
||||
chgrHist.setPayFnsh(this.payFnsh);
|
||||
chgrHist.setRechGwh(this.rechgWh);
|
||||
chgrHist.setChId(this.chId);
|
||||
return chgrHist;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package net.jwsi.jcms.vpp.chgrHist;
|
||||
|
||||
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.api.ApiClient;
|
||||
import net.jwsi.jcms.vpp.api.ApiResponse;
|
||||
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.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ChgrHistService extends BaseService<ChgrHist, Integer, ChgrHistRepository> {
|
||||
|
||||
|
||||
private final ApiClient apiClient;
|
||||
|
||||
public ChgrHistService(ChgrHistRepository repository, ApiClient apiClient) {
|
||||
super(repository);
|
||||
this.apiClient = apiClient;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Scheduled(cron = "0 */10 * * * *")
|
||||
public void getChgrHist() {
|
||||
|
||||
String startDt = LocalDate.now().toString();
|
||||
ApiResponse<List<ChgrHistResponseDto>> response = apiClient.fetchChgrHist(startDt, "50");
|
||||
|
||||
if (response == null || !Integer.valueOf(200).equals(response.getCode())) {
|
||||
String msg = (response != null) ? response.getMessage() : "응답 없음";
|
||||
throw new RuntimeException("API 호출 실패: " + msg);
|
||||
}
|
||||
|
||||
List<ChgrHistResponseDto> dtoList = response.getData();
|
||||
if (dtoList == null || dtoList.isEmpty()) {
|
||||
log.info("가져온 충전이력 데이터가 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
List<ChgrHist> entityList = dtoList.stream()
|
||||
.map(ChgrHistResponseDto::toEntity)
|
||||
.toList();
|
||||
|
||||
int insertCount = 0;
|
||||
for (ChgrHist h : entityList) {
|
||||
|
||||
boolean isExist = ((ChgrHistRepository) super.repository).existsByChgrIdAndChIdAndRechgEDt(h.getChgrId(), h.getChId(),h.getRechgEDt());
|
||||
|
||||
if (!isExist) {
|
||||
super.insert(h);
|
||||
insertCount++;
|
||||
}
|
||||
}
|
||||
|
||||
log.info("충전이력 수집 완료: API 응답 {}건 중 신규 저장 {}건", entityList.size(), insertCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer getUserId() {
|
||||
return HttpUtil.getUserId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getUserName(Integer id) {
|
||||
return LoginUtil.getUserName(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChgrHist newSearchParam(ChgrHist chgrHist) {
|
||||
if (chgrHist == null) {
|
||||
chgrHist = new ChgrHist();
|
||||
}
|
||||
return chgrHist;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnumSqlType useSqlTyp() {
|
||||
return EnumSqlType.SPECIFICATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Specification<ChgrHist> getSpecification(ChgrHist chgrHist) {
|
||||
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
|
||||
protected Page<ChgrHist> _selectPage(ChgrHist chgrHist) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ChgrHist> _selectList(ChgrHist chgrHist) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.jwsi.jcms.vpp.provider;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import lombok.Getter;
|
||||
@@ -7,26 +8,51 @@ import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import net.jwsi.jcms.base.BaseStEntity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@Entity(name="vpp_provider")
|
||||
public class Provider extends BaseStEntity {
|
||||
@Id
|
||||
@Column(name="provider_id")
|
||||
private String providerId;
|
||||
|
||||
@Column(name="PROVIDER_NM")
|
||||
private String providerNm;
|
||||
|
||||
@Column(name="biz_tax_id")
|
||||
private String bizTaxId;
|
||||
|
||||
@Column(name="CEO")
|
||||
private String ceo;
|
||||
|
||||
@Column(name="TEL")
|
||||
private String tel;
|
||||
|
||||
@Column(name="FAX")
|
||||
private String fax;
|
||||
|
||||
@Column(name="EMAIL")
|
||||
private String email;
|
||||
|
||||
@Column(name="MGR_NM")
|
||||
private String mgrNm;
|
||||
|
||||
@Column(name="MGR_TEL")
|
||||
private String mgrTel;
|
||||
|
||||
@Column(name="EMGNCY_TEL")
|
||||
private String emgncyTel;
|
||||
|
||||
@Column(name="ZIP_CODE")
|
||||
private String zipCode;
|
||||
|
||||
@Column(name = "ADDR_RD_M")
|
||||
private String addrRdM;
|
||||
|
||||
@Column(name = "ADDR_RD_D")
|
||||
private String addrRdD;
|
||||
|
||||
@Column(name="REMARK")
|
||||
private String remark;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,62 @@
|
||||
package net.jwsi.jcms.vpp.provider;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.jwsi.jcms.base.BaseController;
|
||||
import net.jwsi.jcms.exception.JsonDataException;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Controller
|
||||
@RequestMapping("/${jcms.manager-path:system}/provider/")
|
||||
public class ProviderController extends BaseController<Provider, String, ProviderRepository, ProviderService> {
|
||||
public ProviderController(ProviderService service) {
|
||||
private final ProviderService providerService;
|
||||
private final String path = "/system/provider/";
|
||||
|
||||
public ProviderController(ProviderService service, ProviderService providerService) {
|
||||
super(service);
|
||||
this.providerService = providerService;
|
||||
}
|
||||
|
||||
@GetMapping("list")
|
||||
public String list() {
|
||||
return path + "list";
|
||||
}
|
||||
|
||||
@PostMapping("list.json")
|
||||
@ResponseBody
|
||||
public Map<String, Object> list(Provider provider) {
|
||||
return svcSelectList(provider);
|
||||
}
|
||||
|
||||
@PostMapping("view.json")
|
||||
@ResponseBody
|
||||
public Map<String, Object> viewData(Provider provider) throws JsonDataException {
|
||||
return svcSelectOne(provider);
|
||||
}
|
||||
|
||||
@PostMapping("insert.json")
|
||||
@ResponseBody
|
||||
public Map<String, Object> insert(Provider provider) throws JsonDataException {
|
||||
return svcInsert(provider);
|
||||
}
|
||||
|
||||
@PostMapping("update.json")
|
||||
@ResponseBody
|
||||
public Map<String, Object> update(Provider provider) throws JsonDataException {
|
||||
return svcUpdate(provider);
|
||||
}
|
||||
|
||||
@PostMapping("delete.json")
|
||||
@ResponseBody
|
||||
public Map<String, Object> delete(Provider provider) throws JsonDataException {
|
||||
return svcDelete(provider.getProviderId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,4 +18,17 @@ public class ProviderDto {
|
||||
private String email;
|
||||
private String zipCode;
|
||||
private String addr;
|
||||
|
||||
public Provider toEntity() {
|
||||
Provider provider = new Provider();
|
||||
provider.setProviderId(this.providerId);
|
||||
provider.setProviderNm(this.providerNm);
|
||||
provider.setBizTaxId(this.bizTaxId);
|
||||
provider.setCeo(this.ceo);
|
||||
provider.setTel(this.tel);
|
||||
provider.setFax(this.fax);
|
||||
provider.setEmail(this.email);
|
||||
provider.setZipCode(this.zipCode);
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package net.jwsi.jcms.vpp.provider;
|
||||
|
||||
import net.jwsi.jcms.base.BaseRecStRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface ProviderRepository extends BaseRecStRepository<Provider, String> {
|
||||
}
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
package net.jwsi.jcms.vpp.provider;
|
||||
|
||||
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.api.ApiClient;
|
||||
import net.jwsi.jcms.vpp.api.ApiResponse;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
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.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@@ -18,40 +22,71 @@ public class ProviderService extends BaseService<Provider, String, ProviderRepos
|
||||
|
||||
private final ApiClient apiClient;
|
||||
|
||||
@Value("${jcms.api.base-url}")
|
||||
private String baseUrl;
|
||||
|
||||
@Value("${jcms.api.token}")
|
||||
private String apiToken;
|
||||
|
||||
public ProviderService(ProviderRepository repository, ApiClient apiClient) {
|
||||
super(repository);
|
||||
this.apiClient = apiClient;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Scheduled(cron = "0 0 0 * * *")
|
||||
public void getProviders() {
|
||||
ApiResponse<List<ProviderDto>> response = apiClient.fetchProviders();
|
||||
|
||||
if (response == null || !Integer.valueOf(200).equals(response.getCode())) {
|
||||
String msg = (response != null) ? response.getMessage() : "응답 없음";
|
||||
throw new RuntimeException("API 호출 실패: " + msg);
|
||||
}
|
||||
|
||||
List<ProviderDto> dtoList = response.getData();
|
||||
if (dtoList == null || dtoList.isEmpty()) {
|
||||
log.info("가져온 충전사업자 데이터가 없습니다.");
|
||||
}
|
||||
|
||||
List<Provider> entityList = dtoList.stream()
|
||||
.map(ProviderDto::toEntity)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Provider> savedProviders = super.repository.saveAll(entityList);
|
||||
|
||||
log.info("총 {}건의 충전사업자 데이터가 DB에 동기화되었습니다.", savedProviders.size());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Integer getUserId() {
|
||||
return null;
|
||||
return HttpUtil.getUserId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getUserName(Integer id) {
|
||||
return null;
|
||||
return LoginUtil.getUserName(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Provider newSearchParam(Provider provider) {
|
||||
return null;
|
||||
if (provider == null) {
|
||||
provider = new Provider();
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnumSqlType useSqlTyp() {
|
||||
return null;
|
||||
return EnumSqlType.SPECIFICATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Specification<Provider> getSpecification(Provider provider) {
|
||||
return null;
|
||||
protected Specification<Provider> getSpecification(Provider srch) {
|
||||
if (srch == null) return null;
|
||||
|
||||
Specification<Provider> rst = null;
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
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;
|
||||
|
||||
public class ProviderSpecification {
|
||||
|
||||
public static Specification<Provider> providerNm(String providerNm) {
|
||||
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,114 @@
|
||||
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());
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
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());
|
||||
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);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package net.jwsi.jcms.vpp.rechgingList;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import net.jwsi.jcms.base.BaseStEntity;
|
||||
|
||||
@NoArgsConstructor
|
||||
@Setter
|
||||
@Getter
|
||||
@ToString
|
||||
@Entity(name = "vpp_rechging_list")
|
||||
public class RechgingList extends BaseStEntity {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "RECHGING_LIST_ID")
|
||||
private Long rechgingListId;
|
||||
|
||||
@Column(name = "SEARCH_DT")
|
||||
private String searchDt;
|
||||
|
||||
@Column(name = "PROVIDER_ID")
|
||||
private String providerId;
|
||||
|
||||
@Column(name = "ST_ID")
|
||||
private String stId;
|
||||
|
||||
@Column(name = "CHGR_ID")
|
||||
private String chgrId;
|
||||
|
||||
@Column(name = "CH_ID")
|
||||
private Integer chId;
|
||||
|
||||
@Column(name = "PLUG_TYPE")
|
||||
private String plugType;
|
||||
|
||||
@Column(name = "MEM_AUTH_INPUT_NO")
|
||||
private String memAuthInputNo;
|
||||
|
||||
@Column(name = "CREDIT_P_PAY_TRX_NO")
|
||||
private String creditPPayTrxNo;
|
||||
|
||||
@Column(name = "CREDIT_P_PAY_TRX_DT")
|
||||
private String creditPPayTrxDt;
|
||||
|
||||
@Column(name = "RECHG_SDT")
|
||||
private String rechgSdt;
|
||||
|
||||
@Column(name = "RECHG_REMAIN_TIME")
|
||||
private String rechgRemainTime;
|
||||
|
||||
@Column(name = "RECHGING_WH")
|
||||
private Integer rechgingWh;
|
||||
|
||||
@Column(name = "RECHG_DEMAND_AMT")
|
||||
private Integer rechgDemandAmt;
|
||||
|
||||
@Column(name = "RECHGING_AMT")
|
||||
private Integer rechgingAmt;
|
||||
|
||||
@Column(name = "PAY_TYPE")
|
||||
private String payType;
|
||||
|
||||
@Column(name = "INTEGRATED_WH")
|
||||
private Double integratedWh;
|
||||
|
||||
@Column(name = "DC_POWER_WH")
|
||||
private Double dcPowerWh;
|
||||
|
||||
@Column(name = "AC_POWER_WH")
|
||||
private Double AcPowerWh;
|
||||
|
||||
@Column(name = "CURR_VOLT")
|
||||
private Double currVolt;
|
||||
|
||||
@Column(name = "CURR_C")
|
||||
private Double currC;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package net.jwsi.jcms.vpp.rechgingList;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.jwsi.jcms.base.BaseController;
|
||||
import net.jwsi.jcms.exception.JsonDataException;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Controller
|
||||
@Slf4j
|
||||
@RequestMapping("/cms/rechgingList/")
|
||||
public class RechgingListController extends BaseController<RechgingList, Long, RechgingListRepository, RechgingListService> {
|
||||
|
||||
private final String path = "/system/rechgingList/";
|
||||
|
||||
private final RechgingListService rechgingListService;
|
||||
|
||||
public RechgingListController(RechgingListService service, RechgingListService rechgingListService) {
|
||||
super(service);
|
||||
this.rechgingListService = rechgingListService;
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("list")
|
||||
public String list() throws IllegalAccessException {
|
||||
return path + "list";
|
||||
}
|
||||
|
||||
@PostMapping("list.json")
|
||||
@ResponseBody
|
||||
public Map<String, Object> list(RechgingList rechgingList) {
|
||||
return svcSelectList(rechgingList);
|
||||
}
|
||||
|
||||
@PostMapping("view.json")
|
||||
@ResponseBody
|
||||
public Map<String, Object> viewData(RechgingList rechgingList) throws JsonDataException {
|
||||
return svcSelectList(rechgingList);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void chkSelectList(List<RechgingList> list) throws JsonDataException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void chkSelect(RechgingList data) throws JsonDataException {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package net.jwsi.jcms.vpp.rechgingList;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class RechgingListDto {
|
||||
private Long rechgingListId;
|
||||
private String searchDt;
|
||||
private String providerId;
|
||||
private String stId;
|
||||
private String chgrId;
|
||||
private Integer chId;
|
||||
private String plugType;
|
||||
private String memAuthInputNo;
|
||||
private String creditPPayTrxNo;
|
||||
private String creditPPayTrxDt;
|
||||
private String rechgSdt;
|
||||
private String rechgRemainTime;
|
||||
private Integer rechgingWh;
|
||||
private Integer rechgDemandAmt;
|
||||
private Integer rechgingAmt;
|
||||
private String payType;
|
||||
private Double integratedWh;
|
||||
private Double dcPowerWh;
|
||||
private Double acPowerWh;
|
||||
private Double currVolt;
|
||||
private Double currC;
|
||||
|
||||
public RechgingList toEntity() {
|
||||
RechgingList rechgingList = new RechgingList();
|
||||
rechgingList.setRechgingListId(this.rechgingListId);
|
||||
rechgingList.setSearchDt(this.searchDt);
|
||||
rechgingList.setProviderId(this.providerId);
|
||||
rechgingList.setStId(this.stId);
|
||||
rechgingList.setChgrId(this.chgrId);
|
||||
rechgingList.setChId(this.chId);
|
||||
rechgingList.setPlugType(this.plugType);
|
||||
rechgingList.setMemAuthInputNo(this.memAuthInputNo);
|
||||
rechgingList.setCreditPPayTrxNo(this.creditPPayTrxNo);
|
||||
rechgingList.setCreditPPayTrxDt(this.creditPPayTrxDt);
|
||||
rechgingList.setRechgSdt(this.rechgSdt);
|
||||
rechgingList.setRechgRemainTime(this.rechgRemainTime);
|
||||
rechgingList.setRechgingWh(this.rechgingWh);
|
||||
rechgingList.setRechgDemandAmt(this.rechgDemandAmt);
|
||||
rechgingList.setRechgingAmt(this.rechgingAmt);
|
||||
rechgingList.setPayType(this.payType);
|
||||
rechgingList.setIntegratedWh(this.integratedWh);
|
||||
rechgingList.setDcPowerWh(this.dcPowerWh);
|
||||
rechgingList.setAcPowerWh(this.acPowerWh);
|
||||
rechgingList.setCurrVolt(this.currVolt);
|
||||
rechgingList.setCurrC(this.currC);
|
||||
return rechgingList;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package net.jwsi.jcms.vpp.rechgingList;
|
||||
|
||||
import net.jwsi.jcms.base.BaseRecStRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface RechgingListRepository extends BaseRecStRepository<RechgingList, Long> {
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package net.jwsi.jcms.vpp.rechgingList;
|
||||
|
||||
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.api.ApiClient;
|
||||
import net.jwsi.jcms.vpp.api.ApiResponse;
|
||||
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.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class RechgingListService extends BaseService<RechgingList, Long, RechgingListRepository> {
|
||||
|
||||
private final ApiClient apiClient;
|
||||
|
||||
public RechgingListService(RechgingListRepository repository, ApiClient apiClient) {
|
||||
super(repository);
|
||||
this.apiClient = apiClient;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Scheduled(cron = "0 0/15 * * * *")
|
||||
public void getRechgingList() {
|
||||
ApiResponse<List<RechgingListDto>> response = apiClient.fetchRechgingList();
|
||||
|
||||
if (response == null || !Integer.valueOf(200).equals(response.getCode())) {
|
||||
String msg =(response != null) ? response.getMessage() : "응답 없음";
|
||||
throw new RuntimeException("API 호출 실패: " + msg);
|
||||
}
|
||||
|
||||
List<RechgingListDto> dtoList = response.getData();
|
||||
if (dtoList == null || dtoList.isEmpty()) {
|
||||
log.info("가져온 충전중 데이터가 없습니다.");
|
||||
}
|
||||
|
||||
List<RechgingList> entityList = dtoList.stream()
|
||||
.map(RechgingListDto::toEntity)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<RechgingList> savedRechgingList = super.repository.saveAll(entityList);
|
||||
|
||||
log.info("총 {}건의 충전중 데이터가 DB에 동기화되었습니다.",savedRechgingList.size());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Integer getUserId() {
|
||||
return HttpUtil.getUserId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getUserName(Integer id) {
|
||||
return LoginUtil.getUserName(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RechgingList newSearchParam(RechgingList rechgingList) {
|
||||
if (rechgingList == null) {
|
||||
rechgingList = new RechgingList();
|
||||
}
|
||||
return rechgingList;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnumSqlType useSqlTyp() {
|
||||
return EnumSqlType.SPECIFICATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Specification<RechgingList> getSpecification(RechgingList srch) {
|
||||
if (srch == null) return null;
|
||||
|
||||
Specification<RechgingList> rst = null;
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Page<RechgingList> _selectPage(RechgingList rechgingList) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<RechgingList> _selectList(RechgingList rechgingList) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import net.jwsi.jcms.base.BaseStEntity;
|
||||
public class Station extends BaseStEntity {
|
||||
|
||||
@Id
|
||||
@Column(name = "st_id")
|
||||
@Column(name = "ST_ID")
|
||||
private String stationId;
|
||||
|
||||
@Column(name = "PROVIDER_ID")
|
||||
@@ -36,19 +36,19 @@ public class Station extends BaseStEntity {
|
||||
@Column(name = "ADDR_LD_D")
|
||||
private String addrLdD;
|
||||
|
||||
@Column(name = "st_facil_tp_cd")
|
||||
private String stFacilTpCd;
|
||||
@Column(name = "ST_FACIL_TP")
|
||||
private String stFacilTp;
|
||||
|
||||
@Column(name = "st_facil_d_tp_cd")
|
||||
private String stFacilDTpCd;
|
||||
@Column(name = "ST_FACIL_D_TP")
|
||||
private String stFacilDTp;
|
||||
|
||||
@Column(name = "parking_fee_yn")
|
||||
@Column(name = "PARKING_FEE_YN")
|
||||
private String parkingFeeYn;
|
||||
|
||||
@Column(name = "PARKING_FEE_DETL")
|
||||
private String parkingFeeDetl;
|
||||
|
||||
@Column(name = "pnu_no")
|
||||
@Column(name = "PNU_NO")
|
||||
private String pnuNo;
|
||||
|
||||
@Column(name = "PNU_DO_NM")
|
||||
|
||||
@@ -27,15 +27,37 @@ public class StationController extends BaseController<Station,String,StationRepo
|
||||
|
||||
@GetMapping("list")
|
||||
public String list() {
|
||||
stationService.getStations();
|
||||
return path + "list";
|
||||
}
|
||||
@PostMapping("list.json")
|
||||
@ResponseBody
|
||||
public Map<String,Object> list(Station station){
|
||||
stationService.getStations();
|
||||
return svcSelectList(station);
|
||||
}
|
||||
|
||||
@PostMapping("view.json")
|
||||
@ResponseBody
|
||||
public Map<String, Object> viewData(Station station) throws JsonDataException {
|
||||
return svcSelectOne(station);
|
||||
}
|
||||
|
||||
@PostMapping("insert.json")
|
||||
@ResponseBody
|
||||
public Map<String, Object> insert(Station station) throws JsonDataException {
|
||||
return svcInsert(station);
|
||||
}
|
||||
|
||||
@PostMapping("update.json")
|
||||
@ResponseBody
|
||||
public Map<String, Object> update(Station station) throws JsonDataException {
|
||||
return svcUpdate(station);
|
||||
}
|
||||
@PostMapping("delete.json")
|
||||
@ResponseBody
|
||||
public Map<String, Object> delete(Station station) throws JsonDataException {
|
||||
return svcDelete(station.getStationId());
|
||||
}
|
||||
@Override
|
||||
protected void chkSelectList(List list) throws JsonDataException {
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.jwsi.jcms.vpp.station;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@@ -20,17 +21,63 @@ public class StationDto {
|
||||
|
||||
public Station toEntity() {
|
||||
Station station = new Station();
|
||||
station.setStationId(this.stId);
|
||||
station.setProviderId(this.providerId);
|
||||
station.setStationId(this.stId);
|
||||
station.setStNm(this.stNm);
|
||||
station.setAddrRdM(this.addrRd);
|
||||
station.setAddrLdM(this.addrLd);
|
||||
station.setStFacilTpCd(this.stFacilTp);
|
||||
station.setStFacilDTpCd(this.stFacilDTp);
|
||||
|
||||
String[] rdParts = splitAddress(this.addrRd);
|
||||
station.setAddrRdM(rdParts[0]);
|
||||
station.setAddrRdD(rdParts[1]);
|
||||
|
||||
String[] ldParts = splitAddress(this.addrLd);
|
||||
station.setAddrLdM(ldParts[0]);
|
||||
station.setAddrLdD(ldParts[1]);
|
||||
|
||||
station.setStFacilTp(this.stFacilTp);
|
||||
station.setStFacilDTp(this.stFacilDTp);
|
||||
station.setParkingFeeYn(this.parkingFee);
|
||||
station.setParkingFeeDetl(this.parkingFeeDetl);
|
||||
|
||||
station.setParkingFeeDetl(this.parkingFeeDetl != null ? this.parkingFeeDetl : "");
|
||||
station.setPnuNo(this.pnuNo);
|
||||
station.setPnuDoNm(this.pnuNm);
|
||||
|
||||
if (StringUtils.hasText(this.pnuNm)) {
|
||||
String[] pnuTokens = this.pnuNm.trim().split("\\s+");
|
||||
if (pnuTokens.length > 0) station.setPnuDoNm(pnuTokens[0]);
|
||||
if (pnuTokens.length > 1) station.setPnuSiNm(pnuTokens[1]);
|
||||
|
||||
if (pnuTokens.length > 2) {
|
||||
StringBuilder dongBuilder = new StringBuilder();
|
||||
for (int i = 2; i < pnuTokens.length; i++) {
|
||||
dongBuilder.append(pnuTokens[i]).append(" ");
|
||||
}
|
||||
station.setPnuDongNm(dongBuilder.toString().trim());
|
||||
}
|
||||
}
|
||||
|
||||
return station;
|
||||
}
|
||||
|
||||
private String[] splitAddress(String addr) {
|
||||
if (!StringUtils.hasText(addr)) {
|
||||
return new String[]{"", ""};
|
||||
}
|
||||
|
||||
String[] tokens = addr.trim().split("\\s+");
|
||||
StringBuilder main = new StringBuilder();
|
||||
StringBuilder detail = new StringBuilder();
|
||||
boolean foundNumber = false;
|
||||
|
||||
for (String token : tokens) {
|
||||
if (!foundNumber) {
|
||||
main.append(token).append(" ");
|
||||
if (token.matches(".*\\d+.*")) {
|
||||
foundNumber = true;
|
||||
}
|
||||
} else {
|
||||
detail.append(token).append(" ");
|
||||
}
|
||||
}
|
||||
|
||||
return new String[]{main.toString().trim(), detail.toString().trim()};
|
||||
}
|
||||
}
|
||||
@@ -6,15 +6,13 @@ 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.api.ApiResponse;
|
||||
import net.jwsi.jcms.vpp.api.ApiClient;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import net.jwsi.jcms.vpp.api.ApiResponse;
|
||||
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.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -23,20 +21,16 @@ import java.util.stream.Collectors;
|
||||
public class StationService extends BaseService<Station,String,StationRepository> {
|
||||
|
||||
private final ApiClient apiClient;
|
||||
@Value("${jcms.api.base-url}")
|
||||
private String baseUrl;
|
||||
|
||||
@Value("${jcms.api.token}")
|
||||
private String apiToken;
|
||||
public StationService(StationRepository repository, ApiClient apiClient) {
|
||||
super(repository);
|
||||
this.apiClient = apiClient;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public List<Station> getStations() {
|
||||
|
||||
ApiResponse<List<StationDto>> response = apiClient.fetchStations(baseUrl, apiToken);
|
||||
@Scheduled(cron = "0 0 0 * * *")
|
||||
public void getStations() {
|
||||
ApiResponse<List<StationDto>> response = apiClient.fetchStations();
|
||||
|
||||
if (response == null || !Integer.valueOf(200).equals(response.getCode())) {
|
||||
String msg = (response != null) ? response.getMessage() : "응답 없음";
|
||||
@@ -46,7 +40,6 @@ public class StationService extends BaseService<Station,String,StationRepository
|
||||
List<StationDto> dtoList = response.getData();
|
||||
if (dtoList == null || dtoList.isEmpty()) {
|
||||
log.info("가져온 충전소 데이터가 없습니다.");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Station> entityList = dtoList.stream()
|
||||
@@ -57,7 +50,6 @@ public class StationService extends BaseService<Station,String,StationRepository
|
||||
|
||||
log.info("총 {}건의 충전소 데이터가 DB에 동기화되었습니다.", savedStations.size());
|
||||
|
||||
return savedStations;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -82,8 +74,12 @@ public class StationService extends BaseService<Station,String,StationRepository
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Specification<Station> getSpecification(Station station) {
|
||||
return null;
|
||||
protected Specification<Station> getSpecification(Station srch) {
|
||||
if(srch == null) return null;
|
||||
Specification<Station> rst = null;
|
||||
rst = addWhere(rst, srch.getStNm(), StationSpecification.stNm(srch.getStNm()));
|
||||
rst = addWhere(rst, srch.getAddrLdM(), StationSpecification.addrLdM(srch.getAddrLdM()));
|
||||
return rst;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package net.jwsi.jcms.vpp.station;
|
||||
|
||||
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
public class StationSpecification {
|
||||
|
||||
public static Specification<Station> stNm(String stNm) {
|
||||
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+"%");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package net.jwsi.jcms.vpp.utils;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Base64;
|
||||
|
||||
public class JwtUtil {
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public static LocalDateTime getExpirationTime(String jwtToken) {
|
||||
try {
|
||||
String[] parts = jwtToken.split("\\.");
|
||||
if (parts.length < 2) {
|
||||
throw new IllegalArgumentException("올바르지 않은 JWT 토큰 형식입니다.");
|
||||
}
|
||||
|
||||
String payloadJson = new String(Base64.getUrlDecoder().decode(parts[1]), StandardCharsets.UTF_8);
|
||||
|
||||
JsonNode payloadNode = objectMapper.readTree(payloadJson);
|
||||
if (!payloadNode.has("exp")) {
|
||||
throw new IllegalStateException("토큰에 exp(만료시간) 필드가 존재하지 않습니다.");
|
||||
}
|
||||
|
||||
long expSeconds = payloadNode.get("exp").asLong();
|
||||
|
||||
return LocalDateTime.ofInstant(Instant.ofEpochSecond(expSeconds), ZoneId.systemDefault());
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("토큰 만료 시간 추출 실패", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,8 @@ spring:
|
||||
jcms-db:
|
||||
driver-class-name: org.mariadb.jdbc.Driver
|
||||
jdbc-url: jdbc:mariadb://192.168.62.37:3306/jcmsdb
|
||||
username: jcms
|
||||
password: jcms3593
|
||||
username: root
|
||||
password: jcms
|
||||
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
@@ -15,16 +15,16 @@ jasypt:
|
||||
bean: jasyptEncryptor
|
||||
|
||||
jcms:
|
||||
permitted-pattern: /, /util/** , /error/**, /login/**, /public/**
|
||||
permitted-pattern: /, /util/**, /error/**, /login/**, /public/**
|
||||
manager-path: cms
|
||||
attr:
|
||||
main-url: /cms/main
|
||||
new-user-st: 9 # 1: ??, 9:????
|
||||
new-user-st: 9
|
||||
cms-name: VPP
|
||||
cms-title: VPP
|
||||
cms-bg-color: '#499'
|
||||
cms-image-url: /public/images/logo_jcms3.png
|
||||
copyright: Copyright 2026 ??????????? Inc.
|
||||
copyright: Copyright 2026 주식회사 진우시스템 Inc.
|
||||
authenticated-patten: /cms/**, /system/**, /api/**
|
||||
pass-url: /cms/main
|
||||
public-url: ''
|
||||
@@ -33,4 +33,5 @@ jcms:
|
||||
days: 30
|
||||
api:
|
||||
base-url: ${API_BASE_URL:http://dev.jinwoosi.co.kr:8072}
|
||||
token: ${API_TOKEN}
|
||||
user-id: ${API_USER_ID:apiuser}
|
||||
user-pwd: ${API_USER_PWD:apiuser}
|
||||
@@ -0,0 +1,203 @@
|
||||
<!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:140px">충전소 ID</span>
|
||||
<input type="text" id="stIdLike" name="stIdLike" class="form-control" placeholder="충전소 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:140px">충전기 ID</span>
|
||||
<input type="text" id="chgrIdLike" name="chgrIdLike" class="form-control" placeholder="충전기 ID 입력" maxlength="50">
|
||||
</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>
|
||||
<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>
|
||||
<script th:inline="none">
|
||||
const rootPath = "/cms/chgr/";
|
||||
let datatable;
|
||||
|
||||
$(document).ready(function() {
|
||||
const className = "dt-head-center dt-body-center";
|
||||
datatable = newDataTable(
|
||||
"#datatable",
|
||||
rootPath + "list.json",
|
||||
function(d) {
|
||||
if (strUtil.isNotEmpty($('#providerIdLike').val())) {d.providerId = $('#providerIdLike').val();}
|
||||
if (strUtil.isNotEmpty($('#stIdLike').val())) {d.stId = $('#stIdLike').val();}
|
||||
if (strUtil.isNotEmpty($('#chgrIdLike').val())) {d.chgrId = $('#chgrIdLike').val();}
|
||||
if (strUtil.isNotEmpty($('#stNmLike').val())) {d.stNm = $('#stNmLike').val();}
|
||||
if (strUtil.isNotEmpty($('#chgrNmLike').val())) {d.chgrNm = $('#chgrNmLike').val();}
|
||||
if (strUtil.isNotEmpty($('#speedTpLike').val())) {d.speedTp = $('#speedTpLike').val();}
|
||||
if (strUtil.isNotEmpty($('#chgrTpLike').val())) {d.chgrTp = $('#chgrTpLike').val();}
|
||||
return d;
|
||||
},
|
||||
{
|
||||
order: [],
|
||||
columns: [
|
||||
{title: "충전사업자 ID", name: "provider_id", data: "providerId", className},
|
||||
{title: "충전소 ID", name: "st_id", data: "stId", className},
|
||||
{title: "충전기 ID", name: "chgr_id", data: "chgrId", className},
|
||||
{title: "충전소 명", name: "st_nm", data: "stNm", className, orderable: false},
|
||||
{title: "충전기 명", name: "chgr_nm", data: "chgrNm", className, orderable: false},
|
||||
{title: "충전기속도", name: "speed_tp", data: "speedTp", className, orderable: false},
|
||||
{title: "충전기타입", name: "chgr_tp", data: "chgrTp", className, orderable: false},
|
||||
{title: "기능", orderable:false, width:"110px",
|
||||
render: (data, type, row) => {
|
||||
return mkRowDataFunctions({providerId: row['providerId'], stId: row['stId'], chgrId: row['chgrId']}, row['chgrNm'], true, menuRoleModify, menuRoleDel);
|
||||
}
|
||||
}
|
||||
],
|
||||
allCheck: false,
|
||||
});
|
||||
});
|
||||
|
||||
const showModal = (mode, ids, title) => {
|
||||
if(!checkRole(mode)) { alert("권한이 없습니다."); return; }
|
||||
|
||||
let modalInputArray = [
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "providerId",
|
||||
inputName: "providerId",
|
||||
hasOldPk: true,
|
||||
inputLabel: "충전사업자 ID",
|
||||
inputPlaceholder: "충전사업자 ID",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 20,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "stId",
|
||||
inputName: "stId",
|
||||
hasOldPk: true,
|
||||
inputLabel: "충전소 ID",
|
||||
inputPlaceholder: "충전소 ID",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 20,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "chgrId",
|
||||
inputName: "chgrId",
|
||||
hasOldPk: true,
|
||||
inputLabel: "충전기 ID",
|
||||
inputPlaceholder: "충전기 ID",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 20,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "stNm",
|
||||
inputName: "stNm",
|
||||
inputLabel: "충전소 명",
|
||||
inputPlaceholder: "충전소 명",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 50,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "chgrNm",
|
||||
inputName: "chgrNm",
|
||||
inputLabel: "충전기 명",
|
||||
inputPlaceholder: "충전기 명",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 50,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "speedTp",
|
||||
inputName: "speedTp",
|
||||
inputLabel: "충전기속도",
|
||||
inputPlaceholder: "충전기속도",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "chgrTp",
|
||||
inputName: "chgrTp",
|
||||
inputLabel: "충전기타입",
|
||||
inputPlaceholder: "충전기타입",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
})
|
||||
];
|
||||
if(!rootPath){ alert("rootPath를 지정하십시요."); return; }
|
||||
const insertUrl = rootPath+"insert.json";
|
||||
const updateUrl = rootPath+"update.json";
|
||||
const loadUrl = rootPath+"view.json";
|
||||
const deleteUrl = rootPath+"delete.json";
|
||||
const modalWidth = 500;
|
||||
const labelWidth = '90px';
|
||||
if(mode === ModalMode.REGISTER) {
|
||||
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) {
|
||||
newModal(new ModalInfo({modalTitle: "상세정보", inputArray: modalInputArray, modalWidth}), mode, 'main', {loadUrl, updateUrl, ids, labelWidth, callBack: () => { defaultCallback(); }})
|
||||
} else if(mode === ModalMode.DELETE) {
|
||||
newDelete(deleteUrl, ids, title, () => { defaultCallback(); });
|
||||
}
|
||||
const defaultCallback = () => {
|
||||
datatable.ajax.reload();
|
||||
}
|
||||
};
|
||||
|
||||
$("#searchBtn").click(function() {
|
||||
datatable.ajax.reload();
|
||||
});
|
||||
|
||||
$("#resetBtn").click(function() {
|
||||
$("#providerIdLike").val("");
|
||||
$("#stIdLike").val("");
|
||||
$("#chgrIdLike").val("");
|
||||
$("#stNmLike").val("");
|
||||
$("#chgrNmLike").val("");
|
||||
$("#speedTpLike").val("");
|
||||
$("#chgrTpLike").val("");
|
||||
datatable.ajax.reload();
|
||||
});
|
||||
</script>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,282 @@
|
||||
<!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:180px">충전소 아이디</span>
|
||||
<input type="text" id="stIdLike" name="stIdLike" 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">충전기 아이디</span>
|
||||
<input type="text" id="chgrIdLike" name="chgrIdLike" 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">충전상태</span>
|
||||
<select id="ch1RechgStateCdLike" name="ch1RechgStateCdLike" class="form-control"></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:180px">도어상태</span>
|
||||
<select id="ch1DoorStateCdLike" name="ch1DoorStateCdLike" class="form-control"></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:180px">플러그상태</span>
|
||||
<select id="ch1PlugStateCdLike" name="ch1PlugStateCdLike" class="form-control"></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>
|
||||
|
||||
<script th:inline="none">
|
||||
const rootPath = "/cms/chgrCurrState/";
|
||||
let datatable;
|
||||
|
||||
const ch1RechgStateCdCdList = getCdDtlList("rechgState");
|
||||
const ch1DoorStateCdCdList = getCdDtlList("doorState");
|
||||
const ch1PlugStateCdCdList = getCdDtlList("plugState");
|
||||
|
||||
$(document).ready(function() {
|
||||
setSearchCdDtlOptions("ch1RechgStateCdLike", ch1RechgStateCdCdList);
|
||||
setSearchCdDtlOptions("ch1DoorStateCdLike", ch1DoorStateCdCdList);
|
||||
setSearchCdDtlOptions("ch1PlugStateCdLike", ch1PlugStateCdCdList);
|
||||
|
||||
const className = "dt-head-center dt-body-center";
|
||||
datatable = newDataTable(
|
||||
"#datatable",
|
||||
rootPath + "list.json",
|
||||
function(d) {
|
||||
if (strUtil.isNotEmpty($('#providerIdLike').val())) {d.providerId = $('#providerIdLike').val();}
|
||||
if (strUtil.isNotEmpty($('#stIdLike').val())) {d.stId = $('#stIdLike').val();}
|
||||
if (strUtil.isNotEmpty($('#chgrIdLike').val())) {d.chgrId = $('#chgrIdLike').val();}
|
||||
if (strUtil.isNotEmpty($('#stateDtLike').val())) {d.stateDt = $('#stateDtLike').val();}
|
||||
if (strUtil.isNotEmpty($('#opModeLike').val())) {d.opMode = $('#opModeLike').val();}
|
||||
if (strUtil.isNotEmpty($('#ch1RechgStateCdLike').val())) {d.ch1RechgStateCd = $('#ch1RechgStateCdLike').val();}
|
||||
if (strUtil.isNotEmpty($('#ch1DoorStateCdLike').val())) {d.ch1DoorStateCd = $('#ch1DoorStateCdLike').val();}
|
||||
if (strUtil.isNotEmpty($('#ch1PlugStateCdLike').val())) {d.ch1PlugStateCd = $('#ch1PlugStateCdLike').val();}
|
||||
return d;
|
||||
},
|
||||
{
|
||||
order: [],
|
||||
columns: [
|
||||
{title: "사업자 ID", name: "provider_id", data: "providerId", className},
|
||||
{title: "충전소 ID", name: "st_id", data: "stId", className},
|
||||
{title: "충전기 ID", name: "chgr_id", data: "chgrId", className},
|
||||
{title: "운영모드", name: "op_mode", data: "opMode", className, orderable: false},
|
||||
{title: "CH1 상태", orderable: false, className,
|
||||
render: (data, type, row) => {
|
||||
let s1 = getCdDtlDtColNm(ch1RechgStateCdCdList, row["ch1RechgStateCd"]) || "";
|
||||
let s2 = getCdDtlDtColNm(ch1DoorStateCdCdList, row["ch1DoorStateCd"]) || "";
|
||||
let s3 = getCdDtlDtColNm(ch1PlugStateCdCdList, row["ch1PlugStateCd"]) || "";
|
||||
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: "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: "기능", orderable:false, width:"110px",
|
||||
render: (data, type, row) => {
|
||||
return mkRowDataFunctions({providerId: row['providerId'], stId: row['stId'], chgrId: row['chgrId']}, row['stId'], true, menuRoleModify, menuRoleDel);
|
||||
}
|
||||
}
|
||||
],
|
||||
allCheck: false,
|
||||
});
|
||||
});
|
||||
|
||||
const showModal = (mode, ids, title) => {
|
||||
if(!checkRole(mode)) { alert("권한이 없습니다."); return; }
|
||||
|
||||
let modalInputArray = [
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "providerId",
|
||||
inputName: "providerId",
|
||||
hasOldPk: true,
|
||||
inputLabel: "provider_id",
|
||||
inputPlaceholder: "provider_id",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 20,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "stId",
|
||||
inputName: "stId",
|
||||
hasOldPk: true,
|
||||
inputLabel: "st_id",
|
||||
inputPlaceholder: "st_id",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 20,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "chgrId",
|
||||
inputName: "chgrId",
|
||||
hasOldPk: true,
|
||||
inputLabel: "chgr_id",
|
||||
inputPlaceholder: "chgr_id",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 20,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.textarea,
|
||||
inputId: "stateDt",
|
||||
inputName: "stateDt",
|
||||
inputLabel: "상태 일시",
|
||||
inputPlaceholder: "상태 일시",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.textarea,
|
||||
inputId: "opMode",
|
||||
inputName: "opMode",
|
||||
inputLabel: "op_mode_cd",
|
||||
inputPlaceholder: "op_mode_cd",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.select,
|
||||
inputId: "ch1RechgStateCd",
|
||||
inputName: "ch1RechgStateCd",
|
||||
inputLabel: "ch1_rechg_state_cd",
|
||||
inputPlaceholder: "ch1_rechg_state_cd",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
itemArray: toCdDtlItemArray(ch1RechgStateCdCdList),
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.select,
|
||||
inputId: "ch1DoorStateCd",
|
||||
inputName: "ch1DoorStateCd",
|
||||
inputLabel: "ch1_door_state_cd",
|
||||
inputPlaceholder: "ch1_door_state_cd",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
itemArray: toCdDtlItemArray(ch1DoorStateCdCdList),
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.select,
|
||||
inputId: "ch1PlugStateCd",
|
||||
inputName: "ch1PlugStateCd",
|
||||
inputLabel: "ch1_plug_state_cd",
|
||||
inputPlaceholder: "ch1_plug_state_cd",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
itemArray: toCdDtlItemArray(ch1PlugStateCdCdList),
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "integratedKwh",
|
||||
inputName: "integratedKwh",
|
||||
inputLabel: "integrated_kwh",
|
||||
inputPlaceholder: "integrated_kwh",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "dcPowerWh",
|
||||
inputName: "dcPowerWh",
|
||||
inputLabel: "dc_power_wh",
|
||||
inputPlaceholder: "dc_power_wh",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "acPowerWh",
|
||||
inputName: "acPowerWh",
|
||||
inputLabel: "ac_power_wh",
|
||||
inputPlaceholder: "ac_power_wh",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
})
|
||||
];
|
||||
|
||||
if(!rootPath){ alert("rootPath를 지정하십시요."); return; }
|
||||
const insertUrl = rootPath+"insert.json";
|
||||
const updateUrl = rootPath+"update.json";
|
||||
const loadUrl = rootPath+"view.json";
|
||||
const deleteUrl = rootPath+"delete.json";
|
||||
const modalWidth = 500;
|
||||
const labelWidth = '140px';
|
||||
|
||||
if(mode === ModalMode.REGISTER) {
|
||||
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) {
|
||||
newModal(new ModalInfo({modalTitle: "상세정보", inputArray: modalInputArray, modalWidth}), mode, 'main', {loadUrl, updateUrl, ids, labelWidth, callBack: () => { defaultCallback(); }})
|
||||
} else if(mode === ModalMode.DELETE) {
|
||||
newDelete(deleteUrl, ids, title, () => { defaultCallback(); });
|
||||
}
|
||||
const defaultCallback = () => {
|
||||
datatable.ajax.reload();
|
||||
}
|
||||
};
|
||||
|
||||
$("#searchBtn").click(function() {
|
||||
datatable.ajax.reload();
|
||||
});
|
||||
|
||||
$("#resetBtn").click(function() {
|
||||
$("#providerIdLike").val("");
|
||||
$("#stIdLike").val("");
|
||||
$("#chgrIdLike").val("");
|
||||
$("#stateDtLike").val("");
|
||||
$("#opModeLike").val("");
|
||||
$("#ch1RechgStateCdLike").val("");
|
||||
$("#ch1DoorStateCdLike").val("");
|
||||
$("#ch1PlugStateCdLike").val("");
|
||||
datatable.ajax.reload();
|
||||
});
|
||||
</script>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,269 @@
|
||||
<!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:160px">충전소 아이디</span>
|
||||
<input type="text" id="stIdLike" name="stIdLike" 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="chgrIdLike" name="chgrIdLike" class="form-control" placeholder="충전기 명 입력" maxlength="50">
|
||||
</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>
|
||||
<script th:inline="none">
|
||||
const rootPath = "/system/chgrHist/";
|
||||
let datatable;
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
const className = "dt-head-center dt-body-center";
|
||||
datatable = newDataTable(
|
||||
"#datatable",
|
||||
rootPath + "list.json",
|
||||
function(d) {
|
||||
if (strUtil.isNotEmpty($('#stIdLike').val())) {d.stId = $('#stIdLike').val();}
|
||||
if (strUtil.isNotEmpty($('#chgrIdLike').val())) {d.chgrId = $('#chgrIdLike').val();}
|
||||
if (strUtil.isNotEmpty($('#payTpLike').val())) {d.payTp = $('#payTpLike').val();}
|
||||
if (strUtil.isNotEmpty($('#plugTpLike').val())) {d.plugTp = $('#plugTpLike').val();}
|
||||
if (strUtil.isNotEmpty($('#rechgSDtLike').val())) {d.rechgSDt = $('#rechgSDtLike').val();}
|
||||
if (strUtil.isNotEmpty($('#rechgEDtLike').val())) {d.rechgEDt = $('#rechgEDtLike').val();}
|
||||
if (strUtil.isNotEmpty($('#rechgTimeLike').val())) {d.rechgTime = $('#rechgTimeLike').val();}
|
||||
if (strUtil.isNotEmpty($('#payFnshLike').val())) {d.payFnsh = $('#payFnshLike').val();}
|
||||
if (strUtil.isNotEmpty($('#chIdLike').val())) {d.chId = $('#chIdLike').val();}
|
||||
|
||||
return d;
|
||||
},
|
||||
{
|
||||
order: [[4, 'desc']],
|
||||
columns: [
|
||||
{title: "충전소 아이디", name: "ST_ID", data: "stId", className, orderable: false},
|
||||
{title: "충전기 아이디", name: "CHGR_ID", data: "chgrId", className, orderable: false},
|
||||
{title: "결제구분", name: "PAY_TP", data: "payTp", 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_E_DT", data: "rechgEDt", className, orderable: false},
|
||||
{title: "충전시간", name: "RECHG_TIME", data: "rechgTime", className, orderable: false},
|
||||
{title: "충전량", name: "RECH_GWH", data: "rechGwh", className, orderable: false},
|
||||
{title: "충전금액", name: "RECHG_AMT", data: "rechgAmt", className, orderable: false},
|
||||
{title: "신용결제완료구분", name: "PAY_FNSH", data: "payFnsh", className, orderable: false},
|
||||
],
|
||||
allCheck: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
const showModal = (mode, ids, title) => {
|
||||
if(!checkRole(mode)) { alert("권한이 없습니다."); return; }
|
||||
|
||||
let modalInputArray = [
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.hidden,
|
||||
inputId: "chrgHistId",
|
||||
inputName: "chrgHistId",
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "stId",
|
||||
inputName: "stId",
|
||||
inputLabel: "충전소 ID",
|
||||
inputPlaceholder: "충전소 ID",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 20,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "chgrId",
|
||||
inputName: "chgrId",
|
||||
inputLabel: "충전기 ID",
|
||||
inputPlaceholder: "충전기 ID",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 20,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "stId",
|
||||
inputName: "stId",
|
||||
inputLabel: "충전소 명",
|
||||
inputPlaceholder: "충전소 명",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 50,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "chgrId",
|
||||
inputName: "chgrId",
|
||||
inputLabel: "충전기 명",
|
||||
inputPlaceholder: "충전기 명",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 50,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "chId",
|
||||
inputName: "chId",
|
||||
inputLabel: "채널",
|
||||
inputPlaceholder: "채널",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 50,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.textarea,
|
||||
inputId: "payTp",
|
||||
inputName: "payTp",
|
||||
inputLabel: "결제구분",
|
||||
inputPlaceholder: "결제구분",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.textarea,
|
||||
inputId: "plugTp",
|
||||
inputName: "plugTp",
|
||||
inputLabel: "충전기타입 코드",
|
||||
inputPlaceholder: "충전기타입 코드",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.textarea,
|
||||
inputId: "rechgSDt",
|
||||
inputName: "rechgSDt",
|
||||
inputLabel: "충전 시작일시",
|
||||
inputPlaceholder: "충전 시작일시",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.textarea,
|
||||
inputId: "rechgEDt",
|
||||
inputName: "rechgEDt",
|
||||
inputLabel: "충전 종료일시",
|
||||
inputPlaceholder: "충전 종료일시",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.textarea,
|
||||
inputId: "rechgTime",
|
||||
inputName: "rechgTime",
|
||||
inputLabel: "충전시간",
|
||||
inputPlaceholder: "충전시간",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "rechGwh",
|
||||
inputName: "rechGwh",
|
||||
inputLabel: "충전량",
|
||||
inputPlaceholder: "충전량",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "rechgAmt",
|
||||
inputName: "rechgAmt",
|
||||
inputLabel: "충전금액",
|
||||
inputPlaceholder: "충전금액",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.textarea,
|
||||
inputId: "payFnsh",
|
||||
inputName: "payFnsh",
|
||||
inputLabel: "신용결제완료구분",
|
||||
inputPlaceholder: "신용결제완료구분",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
})
|
||||
];
|
||||
if(!rootPath){ alert("rootPath를 지정하십시요."); return; }
|
||||
const insertUrl = rootPath+"insert.json";
|
||||
const updateUrl = rootPath+"update.json";
|
||||
const loadUrl = rootPath+"view.json";
|
||||
const deleteUrl = rootPath+"delete.json";
|
||||
const modalWidth = 500;
|
||||
const labelWidth = '90px';
|
||||
if(mode === ModalMode.REGISTER) {
|
||||
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) {
|
||||
newModal(new ModalInfo({modalTitle: "상세정보", inputArray: modalInputArray, modalWidth}), mode, 'main', {loadUrl, updateUrl, ids, labelWidth, callBack: () => { defaultCallback(); }})
|
||||
} else if(mode === ModalMode.DELETE) {
|
||||
newDelete(deleteUrl, ids, title, () => { defaultCallback(); });
|
||||
}
|
||||
const defaultCallback = () => {
|
||||
datatable.ajax.reload();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
$("#searchBtn").click(function() {
|
||||
datatable.ajax.reload();
|
||||
});
|
||||
|
||||
$("#resetBtn").click(function() {
|
||||
$("#stIdLike").val("");
|
||||
$("#chgrIdLike").val("");
|
||||
datatable.ajax.reload();
|
||||
});
|
||||
</script>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,265 @@
|
||||
<!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:140px">충전사업자명</span>
|
||||
<input type="text" id="providerNmLike" name="providerNmLike" class="form-control" placeholder="충전사업자 명 입력" maxlength="50">
|
||||
</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>
|
||||
<script th:inline="none">
|
||||
const rootPath = "/cms/provider/";
|
||||
let datatable;
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
const className = "dt-head-center dt-body-center";
|
||||
datatable = newDataTable(
|
||||
"#datatable",
|
||||
rootPath + "list.json",
|
||||
function(d) {
|
||||
if (strUtil.isNotEmpty($('#providerNmLike').val())) {d.providerNm = $('#providerNmLike').val();}
|
||||
if (strUtil.isNotEmpty($('#bizTaxIdLike').val())) {d.bizTaxId = $('#bizTaxIdLike').val();}
|
||||
if (strUtil.isNotEmpty($('#ceoLike').val())) {d.ceo = $('#ceoLike').val();}
|
||||
if (strUtil.isNotEmpty($('#telLike').val())) {d.tel = $('#telLike').val();}
|
||||
if (strUtil.isNotEmpty($('#faxLike').val())) {d.fax = $('#faxLike').val();}
|
||||
return d;
|
||||
},
|
||||
{
|
||||
order: [],
|
||||
columns: [
|
||||
{title: "충전사업자 명", name: "PROVIDER_NM", data: "providerNm", className, orderable: false},
|
||||
{title: "사업자번호", name: "biz_tax_id", data: "bizTaxId", className, orderable: false},
|
||||
{title: "대표자", name: "CEO", data: "ceo", className, orderable: false},
|
||||
{title: "전화번호", name: "TEL", data: "tel", className, orderable: false},
|
||||
{title: "팩스번호", name: "FAX", data: "fax", className, orderable: false},
|
||||
{title: "기능", orderable: false, width: "140px", className,
|
||||
render: (data, type, row) => { return mkRowDataFunctions( { providerId: row['providerId'] },row['providerNm'], true, true, true);}
|
||||
}
|
||||
],
|
||||
allCheck: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
const showModal = (mode, ids, title) => {
|
||||
if(!checkRole(mode)) { alert("권한이 없습니다."); return; }
|
||||
|
||||
let modalInputArray = [
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.hidden,
|
||||
inputId: "providerId",
|
||||
inputName: "providerId",
|
||||
hasOldPk: true,
|
||||
inputLabel: "provider_id",
|
||||
inputPlaceholder: "provider_id",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 20,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "providerNm",
|
||||
inputName: "providerNm",
|
||||
inputLabel: "충전사업자 명",
|
||||
inputPlaceholder: "충전사업자 명",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 50,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "bizTaxId",
|
||||
inputName: "bizTaxId",
|
||||
inputLabel: "사업자번호",
|
||||
inputPlaceholder: "사업자번호",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 20,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "ceo",
|
||||
inputName: "ceo",
|
||||
inputLabel: "대표자",
|
||||
inputPlaceholder: "대표자",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 50,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "tel",
|
||||
inputName: "tel",
|
||||
inputLabel: "전화번호",
|
||||
inputPlaceholder: "전화번호",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 30,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "fax",
|
||||
inputName: "fax",
|
||||
inputLabel: "팩스번호",
|
||||
inputPlaceholder: "팩스번호",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 30,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.textarea,
|
||||
inputId: "email",
|
||||
inputName: "email",
|
||||
inputLabel: "이메일",
|
||||
inputPlaceholder: "이메일",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "mgrNm",
|
||||
inputName: "mgrNm",
|
||||
inputLabel: "담당자 명",
|
||||
inputPlaceholder: "담당자 명",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 50,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "mgrTel",
|
||||
inputName: "mgrTel",
|
||||
inputLabel: "담당자 전화번호",
|
||||
inputPlaceholder: "담당자 전화번호",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 30,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "emgncyTel",
|
||||
inputName: "emgncyTel",
|
||||
inputLabel: "긴급 전화번호",
|
||||
inputPlaceholder: "긴급 전화번호",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 30,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "zipCode",
|
||||
inputName: "zipCode",
|
||||
inputLabel: "우편번호",
|
||||
inputPlaceholder: "우편번호",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 7,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.textarea,
|
||||
inputId: "addrRdM",
|
||||
inputName: "addrRdM",
|
||||
inputLabel: "주소 도로명 메인",
|
||||
inputPlaceholder: "주소 도로명 메인",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 255,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.textarea,
|
||||
inputId: "addrRdD",
|
||||
inputName: "addrRdD",
|
||||
inputLabel: "주소 도로명 상세",
|
||||
inputPlaceholder: "주소 도로명 상세",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 255,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.textarea,
|
||||
inputId: "remark",
|
||||
inputName: "remark",
|
||||
inputLabel: "비고",
|
||||
inputPlaceholder: "비고",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
})
|
||||
];
|
||||
if(!rootPath){ alert("rootPath를 지정하십시요."); return; }
|
||||
const insertUrl = rootPath+"insert.json";
|
||||
const updateUrl = rootPath+"update.json";
|
||||
const loadUrl = rootPath+"view.json";
|
||||
const deleteUrl = rootPath+"delete.json";
|
||||
const modalWidth = 500;
|
||||
const labelWidth = '90px';
|
||||
if(mode === ModalMode.REGISTER) {
|
||||
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) {
|
||||
newModal(new ModalInfo({modalTitle: "상세정보", inputArray: modalInputArray, modalWidth}), mode, 'main', {loadUrl, updateUrl, ids, labelWidth, callBack: () => { defaultCallback(); }})
|
||||
} else if(mode === ModalMode.DELETE) {
|
||||
newDelete(deleteUrl, ids, title, () => { defaultCallback(); });
|
||||
}
|
||||
const defaultCallback = () => {
|
||||
datatable.ajax.reload();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
$("#searchBtn").click(function() {
|
||||
datatable.ajax.reload();
|
||||
});
|
||||
|
||||
$("#resetBtn").click(function() {
|
||||
$("#providerNmLike").val("");
|
||||
datatable.ajax.reload();
|
||||
});
|
||||
</script>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,657 @@
|
||||
<!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) => data === "true" || data === 'Y' ? '<span class="text-primary fw-bold">활성</span>' : '<span class="text-secondary">비활성</span>'
|
||||
},
|
||||
{title: "기능", orderable:false, width:"120px",
|
||||
render: (data, type, row) => {
|
||||
return 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';
|
||||
|
||||
// ==========================================
|
||||
// 💡 조회 모드 (VIEW)
|
||||
// ==========================================
|
||||
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 || ''}`);
|
||||
$('#v_isActive').text((data.isActive === "true" || data.isActive === 'Y' || data.active === true) ? '활성화' : '비활성');
|
||||
$('#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 isActive = (c.isActive === true || c.active === true || c.isActive === '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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 💡 등록 모드 (REGISTER)
|
||||
// ==========================================
|
||||
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', {
|
||||
// 🚨 insertUrl을 제거하여 프레임워크 폼 전송 방지
|
||||
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() {
|
||||
const seasonType = $('#i_seasonType').val();
|
||||
return seasonType === '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) {
|
||||
const list = res.data || res.list || [];
|
||||
renderChargerList(list);
|
||||
},
|
||||
error: function() {
|
||||
$('#i_chargerListArea').html('<div class="text-muted small p-2">충전기 목록을 불러오지 못했습니다.</div>');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderChargerList(list) {
|
||||
const mapped = list.map(c => {
|
||||
const chargerCd = `${c.providerId || ''}${c.stId || ''}${c.chgrId || ''}`;
|
||||
return {
|
||||
chargerCd: chargerCd,
|
||||
chargerName: c.chgrNm || chargerCd,
|
||||
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>
|
||||
@@ -0,0 +1,412 @@
|
||||
<!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:300px">요청 일시</span>-->
|
||||
<!-- <input type="text" id="searchDtLike" name="searchDtLike" 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">충전사업자 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="input-group">
|
||||
<span class="input-group-text" style="width:300px">충전소 ID</span>
|
||||
<input type="text" id="stIdLike" name="stIdLike" class="form-control" placeholder="충전소 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:300px">충전기 ID</span>
|
||||
<input type="text" id="chgrIdLike" name="chgrIdLike" class="form-control" placeholder="충전기 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:300px">플러그 타입</span>-->
|
||||
<!-- <input type="text" id="plugTypeLike" name="plugTypeLike" 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="memAuthInputNoLike" name="memAuthInputNoLike" 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="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 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="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>-->
|
||||
<!-- <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 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>
|
||||
<script th:inline="none">
|
||||
const rootPath = "/cms/rechgingList/";
|
||||
let datatable;
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
const className = "dt-head-center dt-body-center";
|
||||
datatable = newDataTable(
|
||||
"#datatable",
|
||||
rootPath + "list.json",
|
||||
function(d) {
|
||||
if (strUtil.isNotEmpty($('#searchDtLike').val())) {d.searchDt = $('#searchDtLike').val();}
|
||||
if (strUtil.isNotEmpty($('#providerIdLike').val())) {d.providerId = $('#providerIdLike').val();}
|
||||
if (strUtil.isNotEmpty($('#stIdLike').val())) {d.stId = $('#stIdLike').val();}
|
||||
if (strUtil.isNotEmpty($('#chgrIdLike').val())) {d.chgrId = $('#chgrIdLike').val();}
|
||||
if (strUtil.isNotEmpty($('#plugTypeLike').val())) {d.plugType = $('#plugTypeLike').val();}
|
||||
if (strUtil.isNotEmpty($('#memAuthInputNoLike').val())) {d.memAuthInputNo = $('#memAuthInputNoLike').val();}
|
||||
if (strUtil.isNotEmpty($('#creditPPayTrxNoLike').val())) {d.creditPPayTrxNo = $('#creditPPayTrxNoLike').val();}
|
||||
if (strUtil.isNotEmpty($('#creditPPayTrxDtLike').val())) {d.creditPPayTrxDt = $('#creditPPayTrxDtLike').val();}
|
||||
if (strUtil.isNotEmpty($('#rechgSdtLike').val())) {d.rechgSdt = $('#rechgSdtLike').val();}
|
||||
if (strUtil.isNotEmpty($('#rechgRemainTimeLike').val())) {d.rechgRemainTime = $('#rechgRemainTimeLike').val();}
|
||||
if (strUtil.isNotEmpty($('#payTypeLike').val())) {d.payType = $('#payTypeLike').val();}
|
||||
return d;
|
||||
},
|
||||
{
|
||||
order: [[0, 'desc'], [2, 'desc'], [3, 'desc'], [4, 'desc']],
|
||||
columns: [
|
||||
{title: "충전중 ID", name: "RECHGING_LIST_ID", data: "rechgingListId", className, visible: false},
|
||||
// {title: "요청 일시", name: "SEARCH_DT", data: "searchDt", className, orderable: false},
|
||||
{title: "충전사업자 ID", name: "PROVIDER_ID", data: "providerId", className},
|
||||
{title: "충전소 ID", name: "ST_ID", data: "stId", className},
|
||||
{title: "충전기 ID", name: "CHGR_ID", data: "chgrId", className},
|
||||
// {title: "채널 ID", name: "CH_ID", data: "chId", 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: "CREDIT_P_PAY_TRX_NO", data: "creditPPayTrxNo", className, orderable: false},
|
||||
{title: "신용카드 승인일시", name: "CREDIT_P_PAY_TRX_DT", data: "creditPPayTrxDt", className, orderable: false},
|
||||
{title: "충전 시작일시", name: "RECHG_SDT", data: "rechgSdt", className, orderable: false},
|
||||
{title: "충전 잔여 시간", name: "RECHG_REMAIN_TIME", data: "rechgRemainTime", className, orderable: false},
|
||||
{title: "진행 충전량", name: "RECHGING_WH", data: "rechgingWh", className, orderable: false},
|
||||
{title: "선결제 금액", name: "RECHG_DEMAND_AMT", data: "rechgDemandAmt", className, orderable: false},
|
||||
{title: "진행 충전 금액", name: "RECHGING_AMT", data: "rechgingAmt", className, orderable: false},
|
||||
{title: "결제타입", name: "PAY_TYPE", data: "payType", className, orderable: false},
|
||||
// {title: "기능", orderable:false, width:"110px",
|
||||
// render: (data, type, row) => {
|
||||
// return mkRowDataFunctions({rechgingListId: row['rechgingListId'], providerId: row['providerId'], stId: row['stId'], chgrId: row['chgrId']}, row['#####Title'], true, menuRoleModify, menuRoleDel);}}
|
||||
],
|
||||
allCheck: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
const showModal = (mode, ids, title) => {
|
||||
if(!checkRole(mode)) { alert("권한이 없습니다."); return; }
|
||||
|
||||
let modalInputArray = [
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.hidden,
|
||||
inputId: "rechgingListId",
|
||||
inputName: "rechgingListId",
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.textarea,
|
||||
inputId: "searchDt",
|
||||
inputName: "searchDt",
|
||||
inputLabel: "요청 일시",
|
||||
inputPlaceholder: "요청 일시",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "providerId",
|
||||
inputName: "providerId",
|
||||
hasOldPk: true,
|
||||
inputLabel: "충전사업자 ID",
|
||||
inputPlaceholder: "충전사업자 ID",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 20,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "stId",
|
||||
inputName: "stId",
|
||||
hasOldPk: true,
|
||||
inputLabel: "충전소 ID",
|
||||
inputPlaceholder: "충전소 ID",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 20,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "chgrId",
|
||||
inputName: "chgrId",
|
||||
hasOldPk: true,
|
||||
inputLabel: "충전기 ID",
|
||||
inputPlaceholder: "충전기 ID",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 20,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "chId",
|
||||
inputName: "chId",
|
||||
inputLabel: "채널 ID",
|
||||
inputPlaceholder: "채널 ID",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.textarea,
|
||||
inputId: "plugType",
|
||||
inputName: "plugType",
|
||||
inputLabel: "플러그 타입",
|
||||
inputPlaceholder: "플러그 타입",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "memAuthInputNo",
|
||||
inputName: "memAuthInputNo",
|
||||
inputLabel: "회원카드번호",
|
||||
inputPlaceholder: "회원카드번호",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 20,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.textarea,
|
||||
inputId: "creditPPayTrxNo",
|
||||
inputName: "creditPPayTrxNo",
|
||||
inputLabel: "신용카드 승인번호",
|
||||
inputPlaceholder: "신용카드 승인번호",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.textarea,
|
||||
inputId: "creditPPayTrxDt",
|
||||
inputName: "creditPPayTrxDt",
|
||||
inputLabel: "신용카드 승인일시",
|
||||
inputPlaceholder: "신용카드 승인일시",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.textarea,
|
||||
inputId: "rechgSdt",
|
||||
inputName: "rechgSdt",
|
||||
inputLabel: "충전 시작일시",
|
||||
inputPlaceholder: "충전 시작일시",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.textarea,
|
||||
inputId: "rechgRemainTime",
|
||||
inputName: "rechgRemainTime",
|
||||
inputLabel: "충전 잔여 시간",
|
||||
inputPlaceholder: "충전 잔여 시간",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "rechgingWh",
|
||||
inputName: "rechgingWh",
|
||||
inputLabel: "진행 충전량",
|
||||
inputPlaceholder: "진행 충전량",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "rechgDemandAmt",
|
||||
inputName: "rechgDemandAmt",
|
||||
inputLabel: "선결제 금액",
|
||||
inputPlaceholder: "선결제 금액",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "rechgingAmt",
|
||||
inputName: "rechgingAmt",
|
||||
inputLabel: "진행 충전 금액",
|
||||
inputPlaceholder: "진행 충전 금액",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.textarea,
|
||||
inputId: "payType",
|
||||
inputName: "payType",
|
||||
inputLabel: "결제타입",
|
||||
inputPlaceholder: "결제타입",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "integratedWh",
|
||||
inputName: "integratedWh",
|
||||
inputLabel: "적산량",
|
||||
inputPlaceholder: "적산량",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "dcPowerWh",
|
||||
inputName: "dcPowerWh",
|
||||
inputLabel: "DC 적산량",
|
||||
inputPlaceholder: "DC 적산량",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "acPowerWh",
|
||||
inputName: "acPowerWh",
|
||||
inputLabel: "AC 적산량",
|
||||
inputPlaceholder: "AC 적산량",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "currVolt",
|
||||
inputName: "currVolt",
|
||||
inputLabel: "현재 전압",
|
||||
inputPlaceholder: "현재 전압",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.text,
|
||||
inputId: "currC",
|
||||
inputName: "currC",
|
||||
inputLabel: "현재 전류",
|
||||
inputPlaceholder: "현재 전류",
|
||||
isReq: false,
|
||||
isEnable: true,
|
||||
})
|
||||
];
|
||||
if(!rootPath){ alert("rootPath를 지정하십시요."); return; }
|
||||
const insertUrl = rootPath+"insert.json";
|
||||
const updateUrl = rootPath+"update.json";
|
||||
const loadUrl = rootPath+"view.json";
|
||||
const deleteUrl = rootPath+"delete.json";
|
||||
const modalWidth = 500;
|
||||
const labelWidth = '100px';
|
||||
if(mode === ModalMode.REGISTER) {
|
||||
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) {
|
||||
newModal(new ModalInfo({modalTitle: "상세정보", inputArray: modalInputArray, modalWidth}), mode, 'main', {loadUrl, updateUrl, ids, labelWidth, callBack: () => { defaultCallback(); }})
|
||||
} else if(mode === ModalMode.DELETE) {
|
||||
newDelete(deleteUrl, ids, title, () => { defaultCallback(); });
|
||||
}
|
||||
const defaultCallback = () => {
|
||||
datatable.ajax.reload();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
$("#searchBtn").click(function() {
|
||||
datatable.ajax.reload();
|
||||
});
|
||||
|
||||
$("#resetBtn").click(function() {
|
||||
$("#searchDtLike").val("");
|
||||
$("#providerIdLike").val("");
|
||||
$("#stIdLike").val("");
|
||||
$("#chgrIdLike").val("");
|
||||
$("#plugTypeLike").val("");
|
||||
$("#memAuthInputNoLike").val("");
|
||||
$("#creditPPayTrxNoLike").val("");
|
||||
$("#creditPPayTrxDtLike").val("");
|
||||
$("#rechgSdtLike").val("");
|
||||
$("#rechgRemainTimeLike").val("");
|
||||
$("#payTypeLike").val("");
|
||||
datatable.ajax.reload();
|
||||
});
|
||||
</script>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
@@ -12,72 +12,12 @@
|
||||
<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:200px">주소 도로명 메인</span>
|
||||
<input type="text" id="addrRdMLike" name="addrRdMLike" 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="addrRdDLike" name="addrRdDLike" 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="addrLdMLike" name="addrLdMLike" 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="addrLdDLike" name="addrLdDLike" 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>-->
|
||||
<!-- <select id="stFacilTpCdLike" name="stFacilTpCdLike" class="form-control"></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:200px">시설 상세 구분 코드</span>-->
|
||||
<!-- <select id="stFacilDTpCdLike" name="stFacilDTpCdLike" class="form-control"></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:200px">주차비 상세정보</span>
|
||||
<input type="text" id="parkingFeeDetlLike" name="parkingFeeDetlLike" 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">PNU 코드</span>
|
||||
<input type="text" id="pnuNoLike" name="pnuNoLike" class="form-control" placeholder="PNU 코드 입력" 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="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 id="searchBoxFooter">
|
||||
@@ -102,27 +42,18 @@
|
||||
</div>
|
||||
</div>
|
||||
<script th:inline="none">
|
||||
const rootPath = "/system/vppStation/";
|
||||
const rootPath = "/cms/station/";
|
||||
let datatable;
|
||||
// const stFacilTpCdCdList = getCdDtlList("####"); const stFacilDTpCdCdList = getCdDtlList("####");
|
||||
|
||||
$(document).ready(function() {
|
||||
// setSearchCdDtlOptions("stFacilTpCdLike", stFacilTpCdCdList);
|
||||
// setSearchCdDtlOptions("stFacilDTpCdLike", stFacilDTpCdCdList);
|
||||
const className = "dt-head-center dt-body-center";
|
||||
datatable = newDataTable(
|
||||
"#datatable",
|
||||
rootPath + "list.json",
|
||||
function(d) {
|
||||
if (strUtil.isNotEmpty($('#stNmLike').val())) {d.stNm = $('#stNmLike').val();}
|
||||
if (strUtil.isNotEmpty($('#addrRdMLike').val())) {d.addrRdM = $('#addrRdMLike').val();}
|
||||
if (strUtil.isNotEmpty($('#addrRdDLike').val())) {d.addrRdD = $('#addrRdDLike').val();}
|
||||
if (strUtil.isNotEmpty($('#addrLdMLike').val())) {d.addrLdM = $('#addrLdMLike').val();}
|
||||
if (strUtil.isNotEmpty($('#addrLdDLike').val())) {d.addrLdD = $('#addrLdDLike').val();}
|
||||
// if (strUtil.isNotEmpty($('#stFacilTpCdLike').val())) {d.stFacilTpCd = $('#stFacilTpCdLike').val();}
|
||||
// if (strUtil.isNotEmpty($('#stFacilDTpCdLike').val())) {d.stFacilDTpCd = $('#stFacilDTpCdLike').val();}
|
||||
if (strUtil.isNotEmpty($('#parkingFeeDetlLike').val())) {d.parkingFeeDetl = $('#parkingFeeDetlLike').val();}
|
||||
if (strUtil.isNotEmpty($('#pnuNoLike').val())) {d.pnuNo = $('#pnuNoLike').val();}
|
||||
if (strUtil.isNotEmpty($('#pnuDoNmLike').val())) {d.pnuDoNm = $('#pnuDoNmLike').val();}
|
||||
if (strUtil.isNotEmpty($('#pnuSiNmLike').val())) {d.pnuSiNm = $('#pnuSiNmLike').val();}
|
||||
if (strUtil.isNotEmpty($('#pnuDongNmLike').val())) {d.pnuDongNm = $('#pnuDongNmLike').val();}
|
||||
@@ -132,21 +63,11 @@
|
||||
order: [],
|
||||
columns: [
|
||||
{title: "충전소명", name: "ST_NM", data: "stNm", className, orderable: false},
|
||||
{title: "주소 도로명 메인", name: "ADDR_RD_M", data: "addrRdM", className, orderable: false},
|
||||
{title: "주소 도로명 상세", name: "ADDR_RD_D", data: "addrRdD", className, orderable: false},
|
||||
{title: "주소 지번 메인", name: "ADDR_LD_M", data: "addrLdM", className, orderable: false},
|
||||
{title: "주소 지번 상세", name: "ADDR_LD_D", data: "addrLdD", className, orderable: false},
|
||||
// {title: "시설 구분 코드", name: "st_facil_tp_cd", data: "stFacilTpCd", className, orderable: false,
|
||||
// render: (data, type, row) => getCdDtlDtColNm(stFacilTpCdCdList, row["stFacilTpCd"]) },
|
||||
// {title: "시설 상세 구분 코드", name: "st_facil_d_tp_cd", data: "stFacilDTpCd", className, orderable: false,
|
||||
// render: (data, type, row) => getCdDtlDtColNm(stFacilDTpCdCdList, row["stFacilDTpCd"]) },
|
||||
{title: "주차비 여부", name: "parking_fee_yn", data: "parkingFeeYn", className, orderable: false},
|
||||
{title: "주차비 상세정보", name: "PARKING_FEE_DETL", data: "parkingFeeDetl", className, orderable: false},
|
||||
{title: "PNU 코드", name: "pnu_no", data: "pnuNo", className, orderable: false},
|
||||
{title: "법정동 도 명", name: "PNU_DO_NM", data: "pnuDoNm", className, orderable: false},
|
||||
{title: "법정동 시 명", name: "PNU_SI_NM", data: "pnuSiNm", className, orderable: false},
|
||||
{title: "법정동 읍면동 명", name: "PNU_DONG_NM", data: "pnuDongNm", className, orderable: false},
|
||||
null
|
||||
{title: "기능", orderable: false, width: "140px", className,
|
||||
render: (data, type, row) => { return mkRowDataFunctions( { stationId: row['stationId'] },row['stNm'], true, true, true);}
|
||||
}
|
||||
],
|
||||
allCheck: false,
|
||||
});
|
||||
@@ -171,8 +92,8 @@
|
||||
}),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.hidden,
|
||||
inputId: "stId",
|
||||
inputName: "stId",
|
||||
inputId: "stationId",
|
||||
inputName: "stationId",
|
||||
hasOldPk: true,
|
||||
inputLabel: "충전소ID",
|
||||
inputPlaceholder: "충전소ID",
|
||||
@@ -236,26 +157,6 @@
|
||||
minLen: 0,
|
||||
maxLen: 250,
|
||||
}),
|
||||
// new ModalInput({
|
||||
// inputType: ModalInputType.select,
|
||||
// inputId: "stFacilTpCd",
|
||||
// inputName: "stFacilTpCd",
|
||||
// inputLabel: "시설 구분 코드",
|
||||
// inputPlaceholder: "시설 구분 코드",
|
||||
// isReq: false,
|
||||
// isEnable: true,
|
||||
// itemArray: toCdDtlItemArray(stFacilTpCdCdList),
|
||||
// }),
|
||||
// new ModalInput({
|
||||
// inputType: ModalInputType.select,
|
||||
// inputId: "stFacilDTpCd",
|
||||
// inputName: "stFacilDTpCd",
|
||||
// inputLabel: "시설 상세 구분 코드",
|
||||
// inputPlaceholder: "시설 상세 구분 코드",
|
||||
// isReq: false,
|
||||
// isEnable: true,
|
||||
// itemArray: toCdDtlItemArray(stFacilDTpCdCdList),
|
||||
// }),
|
||||
new ModalInput({
|
||||
inputType: ModalInputType.switch,
|
||||
inputId: "parkingFeeYn",
|
||||
@@ -352,8 +253,6 @@
|
||||
$("#addrRdDLike").val("");
|
||||
$("#addrLdMLike").val("");
|
||||
$("#addrLdDLike").val("");
|
||||
// $("#stFacilTpCdLike").val("");
|
||||
// $("#stFacilDTpCdLike").val("");
|
||||
$("#parkingFeeDetlLike").val("");
|
||||
$("#pnuNoLike").val("");
|
||||
$("#pnuDoNmLike").val("");
|
||||
|
||||
@@ -23,22 +23,22 @@ class StationServiceTest {
|
||||
@Autowired
|
||||
private StationRepository stationRepository;
|
||||
|
||||
@Test
|
||||
@DisplayName("외부 API를 호출하여 DB에 충전소 정보가 정상 저장되는지 테스트")
|
||||
void saveAndGetStationsTest() {
|
||||
List<Station> savedStations = stationService.getStations();
|
||||
|
||||
System.out.println("====== 저장된 건수: " + savedStations.size() + " ======");
|
||||
|
||||
Assertions.assertThat(savedStations).isNotNull();
|
||||
|
||||
if (!savedStations.isEmpty()) {
|
||||
Station firstStation = savedStations.get(0);
|
||||
Station found = stationRepository.findById(firstStation.getStationId()).orElse(null);
|
||||
|
||||
assertThat(found).isNotNull();
|
||||
assertThat(found.getStationId()).isEqualTo(firstStation.getStationId());
|
||||
System.out.println("====== DB 저장 확인 완료 (Station ID: " + found.getStationId() + ") ======");
|
||||
}
|
||||
}
|
||||
// @Test
|
||||
// @DisplayName("외부 API를 호출하여 DB에 충전소 정보가 정상 저장되는지 테스트")
|
||||
// void saveAndGetStationsTest() {
|
||||
// List<Station> savedStations = stationService.getStations();
|
||||
//
|
||||
// System.out.println("====== 저장된 건수: " + savedStations.size() + " ======");
|
||||
//
|
||||
// Assertions.assertThat(savedStations).isNotNull();
|
||||
//
|
||||
// if (!savedStations.isEmpty()) {
|
||||
// Station firstStation = savedStations.get(0);
|
||||
// Station found = stationRepository.findById(firstStation.getStationId()).orElse(null);
|
||||
//
|
||||
// assertThat(found).isNotNull();
|
||||
// assertThat(found.getStationId()).isEqualTo(firstStation.getStationId());
|
||||
// System.out.println("====== DB 저장 확인 완료 (Station ID: " + found.getStationId() + ") ======");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user