From d42accff75a11bc174ae3e4a762a94868df31d38 Mon Sep 17 00:00:00 2001 From: "Lim\\jun" Date: Fri, 26 Jan 2024 13:50:34 +0900 Subject: [PATCH] =?UTF-8?q?API=20Key=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/admin/standards/ApiKeys.jsx | 224 ++++++++++++++++-- .../standards/AdminStandardsController.java | 108 +++++++++ .../admin/standards/entity/TnApiKey.java | 41 ++++ .../repository/ApiKeyRepository.java | 19 ++ .../standards/service/AdminApiService.java | 47 ++++ 5 files changed, 421 insertions(+), 18 deletions(-) create mode 100644 kcsc-back-end/src/main/java/com/dbnt/kcscbackend/admin/standards/AdminStandardsController.java create mode 100644 kcsc-back-end/src/main/java/com/dbnt/kcscbackend/admin/standards/entity/TnApiKey.java create mode 100644 kcsc-back-end/src/main/java/com/dbnt/kcscbackend/admin/standards/repository/ApiKeyRepository.java create mode 100644 kcsc-back-end/src/main/java/com/dbnt/kcscbackend/admin/standards/service/AdminApiService.java diff --git a/egovframe-template-simple-react-contribution/src/pages/admin/standards/ApiKeys.jsx b/egovframe-template-simple-react-contribution/src/pages/admin/standards/ApiKeys.jsx index 7088968..3475d31 100644 --- a/egovframe-template-simple-react-contribution/src/pages/admin/standards/ApiKeys.jsx +++ b/egovframe-template-simple-react-contribution/src/pages/admin/standards/ApiKeys.jsx @@ -1,30 +1,180 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; +import React, {useState, useEffect, useCallback, useRef, PureComponent} from 'react'; +import { Link, useLocation } from 'react-router-dom'; +import {BarChart, Bar, Rectangle, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer} from 'recharts'; + +import Switch from '@mui/material/Switch'; + +import * as EgovNet from 'api/egovFetch'; import URL from 'constants/url'; import { default as EgovLeftNav } from 'components/leftmenu/EgovLeftNavAdmin'; +import { itemIdxByPage } from 'utils/calc'; +import EgovPaging from 'components/EgovPaging'; + function ApiKeys(props) { + // console.group("EgovAdminPrivacyList"); + // console.log("[Start] EgovAdminPrivacyList ------------------------------"); + // console.log("EgovAdminPrivacyList [props] : ", props); + const location = useLocation(); + // console.log("EgovAdminPrivacyList [location] : ", location); + + // eslint-disable-next-line no-unused-vars + const [searchCondition, setSearchCondition] = useState(location.state?.searchCondition || { pageIndex: 1, searchCnd: '0', searchWrd: '' }); + const [paginationInfo, setPaginationInfo] = useState({}); + const [chartData, setChartData] = useState([]); + const [user_id, setuser_id] = useState([]); + + const [listTag, setListTag] = useState([]); + // const label = { inputProps: { 'aria-label': '사용여부' } }; + + const retrieveList = useCallback((srchCnd) => { + // console.groupCollapsed("EgovAdminUsageList.retrieveList()"); + const retrieveListURL = '/admin/standards/apikey'; + + const requestOptions = { + method: "POST", + headers: { + 'Content-type': 'application/json', + }, + body: JSON.stringify(srchCnd) + } + + EgovNet.requestFetch( + retrieveListURL, + requestOptions, + (resp) => { + setPaginationInfo(resp.result.paginationInfo); + + let mutListTag = []; + listTag.push(

데이터가 없습니다.

); // 게시판 목록 초기값 + + const resultCnt = parseInt(resp.result.resultCnt); + const currentPageNo = resp.result.paginationInfo.pageIndex; + const pageSize = resp.result.paginationInfo.rowCnt; + + const startIndex = (currentPageNo - 1) * pageSize; + const endIndex = Math.min(startIndex + pageSize, resultCnt); + + // 리스트 항목 구성 + for (let index = startIndex; index < endIndex; index++) { + const listIdx = itemIdxByPage(resultCnt, currentPageNo, 0, index); // pageSize 로 넣으면 listIdx값이 2배씩 줄어서 0으로 수정 + const item = resp.result.resultList[index]; + + mutListTag.push( +
+
{item.userId}
+
handleApiKeyChart(item)}>{item.apiKey}
+
{item.startDt} ~ {item.endDt}
+
{item.idntyYn === 'Y' ? handleSwitchToggle(item)} /> : handleSwitchToggle(item)} />}
+
+ ); + } + setListTag(mutListTag); + }, + function (resp) { + console.log("err response : ", resp); + } + ); + // console.groupEnd("EgovAdminPrivacyList.retrieveList()"); + },[listTag]); + + const CustomTooltip = ({ active, payload, label }) => { + if (active && payload && payload.length) { + return ( +
+

API 요청 [{user_id}]

+

{`${label} : ${payload[0].value}`}회

+
+ ); + } + return null; + }; + + const handleSwitchToggle = async (item) => { + try { + const updateApiEndpoint = '/admin/standards/apiupdate'; + const requestOptions = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(item), + }; + + const response = await EgovNet.requestFetch(updateApiEndpoint, requestOptions); + const data = await response.json(); + + if (response.ok) { + console.log('Data updated successfully:', data); + return { success: true, data }; + } else { + console.error('Failed to update data:', data); + return { success: false, error: data }; + } + } catch (error) { + console.error('Error during data update:', error); + return { success: false, error }; + } + }; + + const handleApiKeyChart = (item) => { + try { + const updateApiEndpoint = '/admin/standards/apiDailyChart'; + const requestOptions = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(item), + }; + + EgovNet.requestFetch( + updateApiEndpoint, + requestOptions, + (resp) => { + let chartDataArray = resp.result.resultList.map((item, index) => ({ + logdt: item[0], // Assuming logCnt is the x-axis data + "API 요청수": item[1], // Assuming menuTitle is the y-axis data + })); + setChartData(chartDataArray); + // item.userId 값 넣기 + console.log(`User ID: ${item.userId}`); + setuser_id(item.userId); + }, + function (resp) { + console.log("err response : ", resp); + } + ); + } catch (error) { + console.error('Error during data update:', error); + return { success: false, error }; + } + }; + + useEffect(() => { + retrieveList(searchCondition); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + // console.log("------------------------------EgovAdminPrivacyList [End]"); + // console.groupEnd("EgovAdminPrivacyList"); - const Location = React.memo(function Location() { - return ( -
- -
- ) - }); return (
{/* */} - +
+
    +
  • Home
  • +
  • 사이트관리
  • +
  • 건설기준 관리
  • +
  • API KEY 관리
  • +
+
{/* */}
@@ -35,13 +185,51 @@ function ApiKeys(props) {
{/* */}
-

건설기준 관리

+

API KEY 관리

-

API KEY 관리

+ {/* */} +
+
+ 사용자 + 발급키 + 승인기간 + 승인여부 +
+
+ {listTag} +
+
+ {/* */} - 여기에 구현해주세요. - {/* */} +
+ {/* */} + { + retrieveList({ ...searchCondition, pageIndex: passedPage }) //, searchCnd: cndRef.current.value, searchWrd: wrdRef.current.value + }} /> + {/* */} +
+
+ + + + + + } /> + + } /> + + +
+ {/* */}
diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/admin/standards/AdminStandardsController.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/admin/standards/AdminStandardsController.java new file mode 100644 index 0000000..d0dc80c --- /dev/null +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/admin/standards/AdminStandardsController.java @@ -0,0 +1,108 @@ +package com.dbnt.kcscbackend.admin.standards; + +import com.dbnt.kcscbackend.admin.standards.entity.TnApiKey; +import com.dbnt.kcscbackend.admin.standards.service.AdminApiService; +import com.dbnt.kcscbackend.auth.entity.LoginVO; +import com.dbnt.kcscbackend.config.common.BaseController; +import com.dbnt.kcscbackend.config.common.ResponseCode; +import com.dbnt.kcscbackend.config.common.ResultVO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/admin/standards") +@Tag(name="AdminStandardsController", description = "사이트관리 건설기준관리 메뉴 컨트롤러") +public class AdminStandardsController extends BaseController { + + private final AdminApiService adminApiService; + + @Operation( + summary = "건설기준관리 - API 관리", + description = "API 관리", + tags = {"AdminStandardsController"} + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "조회 성공"), + @ApiResponse(responseCode = "403", description = "인가된 사용자가 아님") + }) + @RequestMapping(method = RequestMethod.POST, value = "/apikey", consumes = MediaType.APPLICATION_JSON_VALUE) + public ResultVO selectApiList(@RequestBody TnApiKey tnApiKey, @AuthenticationPrincipal LoginVO user) + throws Exception { + + ResultVO resultVO = new ResultVO(); + tnApiKey.setQueryInfo(); + Map resultMap = adminApiService.selectApiList(); + int totCnt = Integer.parseInt((String)resultMap.get("resultCnt")); + tnApiKey.setContentCnt(totCnt); + tnApiKey.setPaginationInfo(); + resultMap.put("paginationInfo", tnApiKey); + + resultVO.setResultCode(ResponseCode.SUCCESS.getCode()); + resultVO.setResultMessage(ResponseCode.SUCCESS.getMessage()); + resultVO.setResult(resultMap); + return resultVO; + } + + @Operation( + summary = "API key 관리", + description = "API key 승인여부 수정", + tags = {"AdminStandardsController"} + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "수정 성공"), + @ApiResponse(responseCode = "303", description = "만료된 토큰"), + @ApiResponse(responseCode = "403", description = "인가된 사용자가 아님") + }) + @RequestMapping(method = RequestMethod.POST, value = "/apiupdate", consumes = MediaType.APPLICATION_JSON_VALUE) + public ResultVO modifyApi(@RequestBody TnApiKey tnApiKey, @AuthenticationPrincipal LoginVO user) throws Exception{ + ResultVO resultVO = new ResultVO(); + + TnApiKey existingApiKey = adminApiService.getApiKeyById(tnApiKey.getUserId()); + if (existingApiKey != null) { + existingApiKey.setIdntyYn(existingApiKey.getIdntyYn().equals("Y") ? "N" : "Y"); + adminApiService.modifyApi(existingApiKey); + + resultVO.setResultCode(ResponseCode.SUCCESS.getCode()); + } else { + resultVO.setResultCode(ResponseCode.SAVE_ERROR.getCode()); // 존재하지 않는 user_id에 대한 처리 + } + return resultVO; + } + + @Operation( + summary = "API key Chart", + description = "API key 클릭시 차트", + tags = {"AdminStandardsController"} + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "수정 성공"), + @ApiResponse(responseCode = "303", description = "만료된 토큰"), + @ApiResponse(responseCode = "403", description = "인가된 사용자가 아님") + }) + @RequestMapping(method = RequestMethod.POST, value = "/apiDailyChart", consumes = MediaType.APPLICATION_JSON_VALUE) + public ResultVO ApiChart(@RequestBody Map dateRange, @AuthenticationPrincipal LoginVO user) throws Exception{ + ResultVO resultVO = new ResultVO(); + Map resultMap = new HashMap<>(); + + String UserId = dateRange.get("userId"); + + resultMap.put("resultList", adminApiService.selectApiDailyCount(UserId)); + resultVO.setResultCode(ResponseCode.SUCCESS.getCode()); + resultVO.setResultMessage(ResponseCode.SUCCESS.getMessage()); + resultVO.setResult(resultMap); + return resultVO; + } +} diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/admin/standards/entity/TnApiKey.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/admin/standards/entity/TnApiKey.java new file mode 100644 index 0000000..f565c6f --- /dev/null +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/admin/standards/entity/TnApiKey.java @@ -0,0 +1,41 @@ +package com.dbnt.kcscbackend.admin.standards.entity; + +import com.dbnt.kcscbackend.config.common.BoardParams; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.Accessors; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.*; +import java.time.LocalDate; + +@Getter +@Setter +@Accessors(chain = true) +@Entity +@NoArgsConstructor +@DynamicInsert +@DynamicUpdate +@Table(name = "tn_api_key") +public class TnApiKey extends BoardParams { + @Id + @Column(name = "user_id") + private String userId; + + @Column(name = "api_key") + private String apiKey; + + @Column(name = "idnty_yn") + private String idntyYn; + + @Column(name = "start_dt") + @DateTimeFormat(pattern = "yyyy-MM-dd") + private LocalDate startDt; + + @Column(name = "end_dt") + @DateTimeFormat(pattern = "yyyy-MM-dd") + private LocalDate endDt; +} diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/admin/standards/repository/ApiKeyRepository.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/admin/standards/repository/ApiKeyRepository.java new file mode 100644 index 0000000..cbfcb18 --- /dev/null +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/admin/standards/repository/ApiKeyRepository.java @@ -0,0 +1,19 @@ +package com.dbnt.kcscbackend.admin.standards.repository; + +import com.dbnt.kcscbackend.admin.standards.entity.TnApiKey; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +public interface ApiKeyRepository extends JpaRepository { + List findAllByOrderByEndDtDesc(); + + @Query(value = "SELECT TO_CHAR(access_dt, 'YYYY-MM-DD') as log_dt, count(1) as log_cnt " + + "FROM th_api_log " + + "WHERE access_id = :UserId " + + "GROUP BY TO_CHAR(access_dt, 'YYYY-MM-DD') " + + "ORDER BY log_dt asc", nativeQuery = true) + List selectCountApiDaily(@Param("UserId") String UserId); +} diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/admin/standards/service/AdminApiService.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/admin/standards/service/AdminApiService.java new file mode 100644 index 0000000..e736724 --- /dev/null +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/admin/standards/service/AdminApiService.java @@ -0,0 +1,47 @@ +package com.dbnt.kcscbackend.admin.standards.service; + +import com.dbnt.kcscbackend.admin.standards.entity.TnApiKey; +import com.dbnt.kcscbackend.admin.standards.repository.ApiKeyRepository; +import lombok.RequiredArgsConstructor; +import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +@RequiredArgsConstructor +public class AdminApiService extends EgovAbstractServiceImpl { + private final ApiKeyRepository apiKeyRepository; + + public Map selectApiList() { + Map resultMap = new HashMap<>(); + + long totalRecordCount = apiKeyRepository.count(); // 전체 레코드 수 가져오기 + List apiList = apiKeyRepository.findAllByOrderByEndDtDesc(); // 개인 정보 로그 리스트 가져오기 + + resultMap.put("resultCnt", String.valueOf(totalRecordCount)); // 개수를 resultMap에 추가 + resultMap.put("resultList", apiList); // 결과를 resultMap에 추가 + + return resultMap; + } + + public TnApiKey getApiKeyById(String userId) { return apiKeyRepository.findById(userId).orElse(null); } + + @Transactional + public String modifyApi(TnApiKey tnApiKey) { + try { + apiKeyRepository.save(tnApiKey); + return "API key updated successfully"; + } catch (Exception e) { + return "Failed to update API key"; + } + } + + public List selectApiDailyCount(String UserId) { + return apiKeyRepository.selectCountApiDaily(UserId); + } // 일자별 API 요청횟수 + +}