From 015e94d6e479d1a6de801bc78d6a96fbba3f73d9 Mon Sep 17 00:00:00 2001 From: novice1993 Date: Wed, 13 Sep 2023 15:35:29 +0900 Subject: [PATCH] =?UTF-8?q?[Feat]=20=EB=B3=B4=EC=9C=A0=ED=98=84=EA=B8=88?= =?UTF-8?q?=20=EB=B0=8F=20=EC=B5=9C=EB=8C=80=20=EB=A7=A4=EB=8F=84=EA=B0=80?= =?UTF-8?q?=EB=8A=A5=20=EC=88=98=EB=9F=89=20=EC=84=A4=EC=A0=95=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 주식주문 시 필요한 회원별 보유현금 데이터 렌더링 및 처리 가능 - 매도 거래 관련 최대 거래량 설정 시 현재 각 회원이 보유한 회사별 주식만큼만 설정할 수 있도록 설정 (보유 주식 중 거래량이 없어 거래대기로 분류된 주식 수도 차감) Issues #17 --- .../src/components/CentralChartMenu/Index.tsx | 2 ++ .../components/StockOrderSection/Index.tsx | 24 ++++++++++---- .../StockOrderSection/OrderDecisionBtn.tsx | 9 +++--- .../StockOrderSection/VolumeSetteing.tsx | 32 ++++++++++++++----- client/src/hooks/useGetCash.ts | 32 +++++++++++++++++++ client/src/hooks/useGetHoldingStock.ts | 30 +++++++++++++++++ client/src/hooks/useGetStockOrderRecord.ts | 31 ++++++++++++++++++ client/src/models/stockProps.ts | 20 ++++++++++++ client/src/page/MainPage.tsx | 10 +++--- 9 files changed, 166 insertions(+), 24 deletions(-) create mode 100644 client/src/hooks/useGetCash.ts create mode 100644 client/src/hooks/useGetHoldingStock.ts create mode 100644 client/src/hooks/useGetStockOrderRecord.ts diff --git a/client/src/components/CentralChartMenu/Index.tsx b/client/src/components/CentralChartMenu/Index.tsx index 52598c18..2a84d2f8 100644 --- a/client/src/components/CentralChartMenu/Index.tsx +++ b/client/src/components/CentralChartMenu/Index.tsx @@ -10,6 +10,8 @@ import CompareChartBtn from "./CompareChartBtn"; const UpperMenuBar = () => { const companyId = useSelector((state: StateProps) => state.companyId); + console.log(localStorage.getItem("authToken")); + return (
diff --git a/client/src/components/StockOrderSection/Index.tsx b/client/src/components/StockOrderSection/Index.tsx index c5cc3a10..09b3448c 100644 --- a/client/src/components/StockOrderSection/Index.tsx +++ b/client/src/components/StockOrderSection/Index.tsx @@ -2,6 +2,9 @@ import { useSelector, useDispatch } from "react-redux"; import { styled } from "styled-components"; import useGetStockInfo from "../../hooks/useGetStockInfo"; import useGetStockData from "../../hooks/useGetStockData"; +import useGetCash from "../../hooks/useGetCash"; +import useGetStockOrderRecord from "../../hooks/useGetStockOrderRecord"; +import useGetHoldingStock from "../../hooks/useGetHoldingStock"; import { stockOrderClose } from "../../reducer/StockOrderSet-Reducer"; import { StateProps } from "../../models/stateProps"; import StockOrder from "./StockOrder"; @@ -27,19 +30,26 @@ const StockOrderSection = () => { const { stockInfo, stockInfoLoading, stockInfoError } = useGetStockInfo(companyId); const { stockPrice, stockPriceLoading, stockPriceError } = useGetStockData(companyId); + const { cashLoading, cashError } = useGetCash(); + const { orderRecordLoading, orderRecordError } = useGetStockOrderRecord(); + const { holdingStockLoading, holdingStockError } = useGetHoldingStock(); - // 주식주문 창 닫기 - const handleStockOrderClose = () => { - dispatch(stockOrderClose()); - }; + // fetching 데이터 loading, error 여부 + const isLoading = stockInfoLoading || stockPriceLoading || cashLoading || orderRecordLoading || holdingStockLoading; + const isError = stockInfoError || stockPriceError || cashError || orderRecordError || holdingStockError; // 1) 데이터 로딩 중 - if (stockInfoLoading || stockPriceLoading) { + if (isLoading) { return 로딩 중; } - // 2) 데이터 받아오기 실패 + 성공했으나 빈 데이터일 때 - if (stockInfoError || stockPriceError || stockPrice.length === 0) { + // 주식주문 창 닫기 + const handleStockOrderClose = () => { + dispatch(stockOrderClose()); + }; + + // 2) 데이터 받아오기 실패 or 성공했으나 빈 데이터일 때 + if (isError || stockPrice.length === 0) { return (
diff --git a/client/src/components/StockOrderSection/OrderDecisionBtn.tsx b/client/src/components/StockOrderSection/OrderDecisionBtn.tsx index ee9872df..b1b9732d 100644 --- a/client/src/components/StockOrderSection/OrderDecisionBtn.tsx +++ b/client/src/components/StockOrderSection/OrderDecisionBtn.tsx @@ -1,6 +1,7 @@ import { useState, useEffect } from "react"; import { useSelector, useDispatch } from "react-redux"; import { styled } from "styled-components"; +import useGetCash from "../../hooks/useGetCash"; import { StateProps } from "../../models/stateProps"; import { OrderTypeProps } from "../../models/orderTypeProps"; import { setStockOrderVolume } from "../../reducer/StockOrderVolume-Reducer"; @@ -11,10 +12,10 @@ const availableMoneyText02: string = "원"; const totalAmountText: string = "주문총액"; const totalAmountUnit: string = "원"; -// dummyData -const dummyMoney = 10000000; - const OrderDecisionBtn = () => { + const { cashData } = useGetCash(); + const cash = cashData.toLocaleString(); + const dispatch = useDispatch(); const orderType = useSelector((state: StateProps) => state.stockOrderType); const orderPrice = useSelector((state: StateProps) => state.stockOrderPrice); @@ -40,7 +41,7 @@ const OrderDecisionBtn = () => {
{availableMoneyText01} - {dummyMoney.toLocaleString()} + {cash} {availableMoneyText02} diff --git a/client/src/components/StockOrderSection/VolumeSetteing.tsx b/client/src/components/StockOrderSection/VolumeSetteing.tsx index ec6827dd..a6195be5 100644 --- a/client/src/components/StockOrderSection/VolumeSetteing.tsx +++ b/client/src/components/StockOrderSection/VolumeSetteing.tsx @@ -1,7 +1,12 @@ import { useEffect } from "react"; import { useSelector, useDispatch } from "react-redux"; import { styled } from "styled-components"; +import useGetCash from "../../hooks/useGetCash"; +import useGetHoldingStock from "../../hooks/useGetHoldingStock"; +import useGetStockOrderRecord from "../../hooks/useGetStockOrderRecord"; import { StateProps } from "../../models/stateProps"; +import { HoldingStockProps } from "../../models/stockProps"; +import { OrderRecordProps } from "../../models/stockProps"; import { setStockOrderVolume, plusStockOrderVolume, minusStockOrderVolume } from "../../reducer/StockOrderVolume-Reducer"; const volumeSettingTitle: string = "수량"; @@ -14,17 +19,28 @@ const volumPercentage03: number = 50; const volumPercentage04: number = 100; const percentageUnit: string = "%"; -// dummyData -const dummyMoney: number = 10000000; -const dummyholdingStock = 10; - const VolumeSetting = () => { const dispatch = useDispatch(); + const companyId = useSelector((state: StateProps) => state.companyId); const orderType = useSelector((state: StateProps) => state.stockOrderType); const orderPrice = useSelector((state: StateProps) => state.stockOrderPrice); const orderVolume = useSelector((state: StateProps) => state.stockOrderVolume); - const maximumBuyingVolume = orderPrice !== 0 ? Math.trunc(dummyMoney / orderPrice) : Math.trunc(dummyMoney / 1); + // 거래가능 주식 수 = 보유 주식 수 - 매도 대기 중인 주식 수 + let avaiableSellingStock: number = 0; + + const { cashData } = useGetCash(); + const { holdingStockData } = useGetHoldingStock(); + const { orderRecordData } = useGetStockOrderRecord(); + const holdingCompanyStock = holdingStockData.filter((stock: HoldingStockProps) => stock.companyId === companyId); + + const maximumBuyingVolume = orderPrice !== 0 ? Math.trunc(cashData / orderPrice) : Math.trunc(cashData / 1); + + // 해당 company 보유 주식이 0이 아닐 때 + if (holdingCompanyStock.length !== 0) { + const waitingForSaleStock = orderRecordData.filter((stock: OrderRecordProps) => stock.companyId === companyId && stock.orderTypes === "SELL" && stock.orderStates === "ORDER_WAIT"); + avaiableSellingStock = holdingCompanyStock[0].stockCount - waitingForSaleStock.length; + } const handlePlusOrderVolume = () => { // 매수 -> 증가 버튼 클릭 시, 최대 구매수량 보다 낮으면 개수 1증가 @@ -33,7 +49,7 @@ const VolumeSetting = () => { } // 매도 -> 증가 버튼 클릭 시, 보유 주식수량 보다 낮으면 개수 1증가 if (orderType) { - orderVolume < dummyholdingStock && dispatch(plusStockOrderVolume()); + orderVolume < avaiableSellingStock && dispatch(plusStockOrderVolume()); } }; @@ -81,7 +97,7 @@ const VolumeSetting = () => { // 매도 -> percentage 버튼 클릭 시, 보유 주식수량 내에서 계산 if (orderType) { - const orderVolume = Math.trunc(dummyholdingStock * (volumePerentage / 100)); + const orderVolume = Math.trunc(avaiableSellingStock * (volumePerentage / 100)); dispatch(setStockOrderVolume(orderVolume)); } }; @@ -99,7 +115,7 @@ const VolumeSetting = () => {
{volumeSettingTitle}
{maximumVolumeText01} - {orderType ? dummyholdingStock : maximumBuyingVolume} + {orderType ? avaiableSellingStock : maximumBuyingVolume} {volumeUnit}
diff --git a/client/src/hooks/useGetCash.ts b/client/src/hooks/useGetCash.ts new file mode 100644 index 00000000..0f1b25d7 --- /dev/null +++ b/client/src/hooks/useGetCash.ts @@ -0,0 +1,32 @@ +import { useQuery } from "react-query"; +import { useSelector } from "react-redux"; +import axios from "axios"; +import { StateProps } from "../models/stateProps"; + +// 🔴 API 수정 전으로 임시 파라미터 설정해놓음 +const url = "http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com:8080/cash/3"; + +const useGetCash = () => { + const isLogin = useSelector((state: StateProps) => state.login); + const { data, isLoading, isError } = useQuery("cash", getCashData, { + enabled: isLogin === 1, + }); + + return { cashData: data, cashLoading: isLoading, cashError: isError }; +}; + +export default useGetCash; + +const getCashData = async () => { + const token = localStorage.getItem("authToken"); + const options = { + headers: { + Authorization: `${token}`, + "Content-Type": "application/json", + }, + }; + + const response = await axios.get(url, options); + const cash = await response.data.money; + return cash; +}; diff --git a/client/src/hooks/useGetHoldingStock.ts b/client/src/hooks/useGetHoldingStock.ts new file mode 100644 index 00000000..b9ac7431 --- /dev/null +++ b/client/src/hooks/useGetHoldingStock.ts @@ -0,0 +1,30 @@ +import { useQuery } from "react-query"; +import { useSelector } from "react-redux"; +import axios from "axios"; +import { StateProps } from "../models/stateProps"; + +const url = "http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com:8080/stock/stockholds"; + +const useGetHoldingStock = () => { + const isLogin = useSelector((state: StateProps) => state.login); + const { data, isLoading, isError } = useQuery("holdingStock", getHoldingStock, { + enabled: isLogin === 1, + }); + return { holdingStockData: data, holdingStockLoading: isLoading, holdingStockError: isError }; +}; + +export default useGetHoldingStock; + +const getHoldingStock = async () => { + const authToken = localStorage.getItem("authToken"); + const options = { + headers: { + Authorization: `${authToken}`, + "Content-Type": "application/json", + }, + }; + + const response = await axios.get(url, options); + const holdingStock = await response.data; + return holdingStock; +}; diff --git a/client/src/hooks/useGetStockOrderRecord.ts b/client/src/hooks/useGetStockOrderRecord.ts new file mode 100644 index 00000000..961b38d9 --- /dev/null +++ b/client/src/hooks/useGetStockOrderRecord.ts @@ -0,0 +1,31 @@ +import { useQuery } from "react-query"; +import { useSelector } from "react-redux"; +import axios from "axios"; +import { StateProps } from "../models/stateProps"; + +const url = "http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com:8080/stock/stockorders"; + +const useGetStockOrderRecord = () => { + const isLogin = useSelector((state: StateProps) => state.login); + const { data, isLoading, isError } = useQuery("record", getOrderRecord, { + enabled: isLogin === 1, + }); + + return { orderRecordData: data, orderRecordLoading: isLoading, orderRecordError: isError }; +}; + +export default useGetStockOrderRecord; + +const getOrderRecord = async () => { + const authToken = localStorage.getItem("authToken"); + const options = { + headers: { + Authorization: `${authToken}`, + "Content-Type": "application/json", + }, + }; + + const response = await axios.get(url, options); + const orderRecord = await response.data; + return orderRecord; +}; diff --git a/client/src/models/stockProps.ts b/client/src/models/stockProps.ts index 37f2ecd5..84dc4fb2 100644 --- a/client/src/models/stockProps.ts +++ b/client/src/models/stockProps.ts @@ -8,3 +8,23 @@ export interface StockProps { stck_hgpr: string; stck_lwpr: string; } + +export interface HoldingStockProps { + companyId: number; + companyKorName: string; + memberId: number; + percentage: number; + price: number; + stockCount: number; + stockHoldId: number; +} + +export interface OrderRecordProps { + companyId: number; + memberId: number; + orderStates: string; + orderTypes: string; + price: number; + stockCount: number; + stockOrderId: number; +} diff --git a/client/src/page/MainPage.tsx b/client/src/page/MainPage.tsx index 6c21b5e6..c7d0e483 100644 --- a/client/src/page/MainPage.tsx +++ b/client/src/page/MainPage.tsx @@ -21,7 +21,6 @@ import { TabContainerPage } from "./TabPages/TabContainerPage"; // 🔴 로그아웃 관련 action 함수 import { setLogoutState } from "../reducer/member/loginSlice"; -import { setLoginState } from "../reducer/member/loginSlice"; const MainPage = () => { const expandScreen = useSelector((state: StateProps) => state.expandScreen); @@ -98,11 +97,12 @@ const MainPage = () => { const [isLoggedIn, setIsLoggedIn] = useState(false); // 로그인 상태 관리 useEffect(() => { - const authToken = localStorage.getItem("authToken"); + // const authToken = localStorage.getItem("authToken"); - if (authToken !== null) { - dispatch(setLoginState()); - } + // if (authToken !== null) { + // dispatch(setLoginState()); + // } + localStorage.removeItem("authToken"); }, []); //프로필 모달 열고닫는 매커니즘