pbp-cs 프로세스 정리중

This commit is contained in:
DESKTOP-KUL8TT4\siina 2024-08-30 11:44:03 +09:00
parent 2bbce942e1
commit 4cb994cdbc
13 changed files with 670 additions and 0 deletions

View File

@ -0,0 +1,386 @@
# PBP-CS 모든 프로세스 모음
> 개발 도중 프로세스 및 코드에 대한 이해도가 낮은 이유로 개발이 어려움을 느끼며 차후 개발에서 개발 속도 및 변경 부작용을 없애고 이해도를 높이고자 이 문서를 작성함
<br><br>
### 2024-08-29 (현 하나증권 중심)
**공병훈 교수**{style="font-style:italic"}는 플랫폼이란 "서로 연결된 관계를 맺으며 가치를 만드는 체계"라는 표현을 사용하였다. 상호의존적인 관계를 형성하며 서로 다른 그룹들간의 상호작요을 쉽게 하여 가치를 창조한다.
플랫폼에 대한 자세한 내용은 다음 링크로 확인하면 좋다.
<https://brunch.co.kr/@mobility/62>
<br><br><br>
## Login -> Assets
> [로그인]페이지에서 [내 자산]으로 화면이 넘아갈때의 프로세스를 아래 기술한다.
```mermaid
flowchart LR
Login --> Assets
```
### - Login
```mermaid
---
title: src/app/login/page.tsx
---
classDiagram
class Login{
param: ref, sccofnstcd
}
class LoginTask{
onConfirm?(bool: boolean): void; // 로그 인 완료
}
class CookieInfo{
}
class Spinner{
}
Login *-- LoginTask : Component
Login *-- CookieInfo : Component
Login *-- Spinner : Component
Login <-- LoginTask : onConfirmLogin
```
<br><br>
- 로그인 페이지에서 URL파라미터 증권사코드 sccoFnstCd값 받아서 처리
<br> <font color='green' size="1"><)"270 (하나증권)"></font><br>
그리고 증권사코드를 localStorage에 저장 저장하는 키는 각 2개임
<br> <font color='green' size="1">accessKey, everywhereKey</font><br> accessKey는 웹뷰용 접근 증권사 코드,
everywhereKey는 전방위용 접근 증권사 코드 라고한다...
```javascript
if (sccoFnstCd) {
localStorage.setItem(accessKey, sccoFnstCd.toString());
localStorage.setItem(everywhereKey, sccoFnstCd.toString());
} else {
localStorage.setItem(accessKey, '');
localStorage.setItem(everywhereKey, '');
}
```
> <font color="red">ISSUE:</font> 간헐적 백엔드에 270값 안 넘오는 현상 발견됨
- onConfirmLogin 함수에서는 LoginTask의 onConfirm에 함수 제공.
<br><br>
```javascript
const onConfirmLogin = () => {
if (ref !== null && ref !== undefined && ref !== '') {
router.push(ref);
return;
}
router.push('/assets');
}
```
위의 onConfirmLogin에서 브라우저 url param의 ref를 받아서 유무 처리 후 route 처리.
** 결론적으로 Login 페이지에서 sccoFnstCd(증권사코드)를 localStorage 처리, route처리 하는 기능. {style="color:red"}
<br><br>
```mermaid
classDiagram
class LoginTask{
counselSeq?: number;
onConfirm?(bool: boolean): void; // 로그 인 완료
onBack?(): void; // 화면뒤로
isTest?: boolean;
isMTS?: boolean;
----------default set------
isTest = false, isMTS = false
}
```
<br><br>
```mermaid
---
title: src/components/task/login/LoginTask.tsx
---
classDiagram
class LoginTask{
}
class AlertModal{
}
class PinNumberLoginTask{
onConfirm(): void;
onBack(): void;
onLoginFail(bool: boolean, idx: number): void;
isMTS: boolean;
}
class ReSignupProcess{
onConfirm,
onClose,
isDeviceChange = false,
FailCount,
isPasswordInput = false
}
class SignupProcess{
counselSequence?: string;
onConfirm?(loginCustomerId: string): void;
onClose?(): void;
onBack?(): void;
}
LoginTask --> PinNumberLoginTask : 1) defaultLoginOption === 'pinnumber' <br> && !loginFail <br> && !isTest <br> && !isNewUser
LoginTask --> ReSignupProcess : 2) loginFail <br> && !isTest
LoginTask --> SignupProcess : 3) isNewUser
PinNumberLoginTask --> LoginTask : 4) onConfirmLogin() <br> onLoginFail()
```
<br><br>
- isTest가 왜 존재 하는지? (true로 변경시 빈화면 나오게 됨)
- defaultLoginOption = process.env.NEXT_PUBLIC_DEFAULT_LOGIN_VIEW
<br> # 간편비밀번호 사용 여부 pinnumber or password
<br>NEXT_PUBLIC_DEFAULT_LOGIN_VIEW=pinnumber | password
<br>현재는 pinnumber
- 위의 1), 2), 3)을 하나의 함수로 처리하는게 좋을듯 하다.
4) localStorage의 'KEY'를 가져와서 `getUser` 함수 실행
<br><br><br>
#### PinNumberLoginTask로 로그인이 되었을때
```javascript
const onConfirmLogin = async () => {
const user = localStorage.getItem(KEY);
if (user) {
const decodeUser = decodeAES(user);
await getUser(decodeUser);
} else {
setAlertModal({
title: '로그인 실패',
content: (
<>
아이디가 없습니다. 회원가입을 안하셨다면 회원가입을 하시거나 <br /> 사용 기기를 변경하셨다면 <br /> 본인
인증을 해주신 후 <br /> 로그인을 시도 해주세요.
</>
)
});
}
};
```
```javascript
const getUser = useCallback(
async (customerId: string) => {
setIsLoading(true);
const customer = await getUserInfoApi({
customerId,
sccoFnstCd: '',
role: ''
});
setIsLoading(false);
if (isEmptyString(customer.userId)) {
const msg = `LocalStorage에 아이디가 존재하고, 간편비밀번호 로그인에 성공했지만, DB에 회원정보가 없음(${customerId})`;
commonRecordingLog(recoilFrontEndLog, 'LoginTask', 'api', msg);
setAlertModal({
title: '로그인 실패',
content: '회원정보를 가져올 수 없습니다.\n회원가입을 다시 해주세요.'
});
} else {
await fetch('/api/setSessions', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({customerId: customer.userId, sccoFnstCd})
});
if (fristLoginValue === null || fristLoginValue === undefined) {
if (customer.pushStatusDate !== null && customer.pushStatusDate !== undefined) {
if (customer.pushStatus === '01') {
setToastMessage([
{
id: getUniqueKey(),
text: `${moment(customer.pushStatusDate).format(
'YYYY.MM.DD HH:mm'
)} 이벤트*혜택 알림 수신에 동의하셨습니다.`,
bottom: 70
}
]);
} else {
setToastMessage([
{
id: getUniqueKey(),
text: `${moment(customer.pushStatusDate).format(
'YYYY.MM.DD HH:mm'
)} 이벤트*혜택 알림 수신에 거부하셨습니다.`,
bottom: 70
}
]);
}
}
localStorage.setItem(firstLoginKey, 'Y');
}
setLoginState(customer);
setUserId(customerId || '');
const encodeId = encodeAES(customerId);
localStorage.setItem(KEY, encodeId);
onConfirm?.(true);
}
},
[
setIsLoading,
recoilFrontEndLog,
setAlertModal,
sccoFnstCd,
fristLoginValue,
setLoginState,
setUserId,
KEY,
onConfirm,
firstLoginKey,
setToastMessage
]
);
```
<br><br>
getUserInfoApi API를 호출함 getUserInfo의 내용에 많은 정보가 기록되어 있는 것을 알 수 있음
```javascript
export const getUserInfoApi = async (obj: IUserInfoInput): Promise<IUserInfoOutput> => {
return api.get<IUserInfoOutput>('/customer', {customerId: obj.customerId});
};
```
```javascript
export interface IUserInfoInput {
customerId?: string;
sccoFnstCd: string;
ci?: string;
userId?: string;
role: string;
}
export interface IUserInfoOutput {
sccoFnstCd: string; // * 23.11.10 ADD yeong
securitiesName: string; // * 23.11.10 ADD yeong
userId: string;
ci: string;
di: string;
password: string;
mobileNumber: string;
birth: string;
gender: number;
email: string;
osType?: string;
osPlatform?: string;
roles?: string;
token?: string;
appVersion?: string;
userLevel?: string;
userName: string;
nickName?: string;
activeStep: string;
joinDate?: string;
lastDate?: string;
authCode?: string;
passwordErrorCount: number;
passwordDate: string;
investmentPropensity: InvestmentPropensity; // * 23.11.10 ADD yeong
customerStatusCode: string; // * 23.11.10 ADD yeong
pushStatus: string; // 01 등록, 02 해제
pushStatusDate: string;
}
```
<br><br>
정치영팀장의 계정으로 접속하였을때 아래의 값을 출력함
```json
{
"securitiesName": "하나증권",
"userId": "5000001008",
"userName": "정치영",
"mobileNumber": "01051670729",
"birth": "19790417",
"gender": 1,
"email": null,
"sccoCustomerIdentifyId": null,
"remarks": null,
"passwordErrorCount": 0,
"passwordDate": "2024-08-21 10:51:34",
"activeStep": "20",
"investmentPropensity": {
"investmentPropensityCode": "1",
"investmentPropensity": "공격투자형",
"lastSurveyDate": "20240826",
"remainingDateCount": 361,
"validYn": "Y",
"todayTryCount": 0
},
"pushStatus": "01",
"pushStatusDate": "2024-08-13 14:28:51"
}
```
- getUserInfoApi의 결과 값을 /api/setSessions로 전달하여 내용을 저장
- getUser함수에서 성공하면 LoginTask의 onConfirm을 Login의 onConfirmLogin으로 true를 전달한다.
```mermaid
flowchart LR
node_1("LoginTask")
node_2("Login")
node_1 --"onConfirm={true}"--> node_2
```
Login에 있는 onConfirmLogin을 실행함으로써 /assets으로 route하게됨
<br><br>
### - Assets
```mermaid
---
title: src/app/assets/page.tsx
---
classDiagram
class Assets{
}
class AssetMain{
}
class Spinner{
}
class CookieInfo{
}
class Navigation{
}
class RequestReplyTask{
}
Assets *-- AssetMain
Assets *-- Spinner
Assets *-- CookieInfo
Assets *-- AssetMain
Assets *-- Navigation
Assets *-- RequestReplyTask
```
<br><br>

View File

@ -0,0 +1,18 @@
```mermaid
classDiagram
class Login{
param: ref, sccofnstcd
}
class LoginTask{
counselSeq?: number;
onConfirm?(bool: boolean): void; // 로그 인 완료
onBack?(): void; // 화면뒤로
isTest?: boolean;
isMTS?: boolean;
}
class CookieInfo{
}
class Spinner{
}
```

View File

@ -60,6 +60,10 @@
CS 로그인 정보
USERID_INDEX_20231118
정치영팀장 U2FsdGVkX19+ppz/We/H+2mKzTSmj3vA2PwN3pOVjYQ=
```json
{
"origin": "http://localhost:3200",
@ -790,3 +794,212 @@ module.exports = {
### 약관 변경 사항 (24-08-23 11:30)
>
> 정BJ: 로그인시 팝업으로 뜨는 변경약관 동의는 이제 "필수"이다 이걸 동의하지 않는다면 서비스를 사용할 수 없다.
### HAZELCAST 캐시화된 DB 캐시 Clear
클네임:hazelcast-cluster-dev
210.179.172.127:5050
### 업무 2024-08-27
`/terms/product/roboadvisor` API는 [GET] [POST] 사용이 필요 없어짐
로보어드바이저 투자일임계약 약관 등록은 투자 일음 계약서에 포함되어 있기 때문에 따로 필요 없음 기존의 일임계약서를 대체함
가입시
`/goods/portfolio/investPortfolio`오류..
"해당 계좌는 사용할 수 없는 계좌입니다."
이건 <kbd>InvestTask.tsx</kbd>의 investResult의 변수값을 저 위의 api에 던짐
getUserId `/user/id`
```json
{
"value": "5000003080",
"success": true
}
```
```json
{
"investDivType": "",
"inputDivType": "02",
"account": "39784625010",
"portfolioId": null,
"portfolioName": "김익희님의 포트폴리오 20240827-1619",
"referencePortfolioId": null,
"counselSeq": null,
"strategyId": "",
"totalInvestAmount": 20000000,
"totalUsdInvestAmount": 0,
"strategyList": [
{
"strategyId": "KP014NaN",
"investAmount": 20000000,
"investmentWeight": 1
}
],
"descriptionAgreeSeq": 630,
"investmentTypeDvsn": "01",
"contractAgreeSeq": 631
}
````
api_mpot에서
TC_USER_ACCOUNT의
UID = 5000003080
REQUEST_STATUS_CODE 01 ---> 03
QTDB에서 TC5002MT 테이블에
270 39784625010 Y 20240826184739 100159 2024-02-01 15:48:54.000
이거 생성함
가입이 완료되면 포트폴리오는
TP0010MT에 정보 생성함
하지만 그 생성하는 과정에서 하나증권 api를 타기 때문에 실패!
<br><br><br>
### 업무 2024-08-27
assetMain에서 모든 계좌에 가입되어 있는 포트폴리오가지고
포트폴리오를 조회해서
포트폴리오의 투자 문서를 가져올꺼임
portfolioDetail에서 onClickInvestmentDocs를 보면 setInvestmentDocsData에
```javascript
ptflId: portfolio.portfolioId,
buyDate: portfolioDetail?.accountPortfolioDetailInfo[0].firstDate || '',
type: '3', // 보고서 타입 [1:보고서리스트,2:보고서상세]
year: '',
quarter: '',
account: portfolio.accountNo
```
이런식으로 데이터 만들어서
InvestmentDocsTask에 값을 보내고 있음
그리고 각각 InvestmentDoc, StrategyDoc, DiscretionaryContractDoc, OperationaryReportDoc로 보내서 가입시 동의 또는 운영시 작성된 약관, 리포트 리스트를 보여줌
```javascript
/**
* Get 전략 설명서
* @returns
* @param params
*/
export const getInvestmentStrategyReport = async (
params: IInvestmentStrategyCommonParam
): Promise<IInvestmentStrategyContractDoc> => {
return api.get<IInvestmentStrategyContractDoc>('/doc/strategy', params);
};
/**
* Get 운용 보고서
* @returns
* @param params
*/
export const getInvestmentOperationReport = async (
params: IOperationParams
): Promise<IInvestmentOperationaryContractDoc[]> => {
return api.get<IInvestmentOperationaryContractDoc[]>('/doc/report/operation', params);
};
/**
* Get 일임 계약서
* @returns
* @param params
*/
export const getInvestmentContractReport = async (
params: IInvestmentDiscretionaryDocParams
): Promise<IInvestmentStrategyContractDoc> => {
return api.get<IInvestmentStrategyContractDoc>('/doc/discretion', params);
};
```
위의 api로 문서 호출
```json
// PortfolioDetailData
{
"portfolioName": "정치영님의 포트폴리오 20240807-1603",
"portfolioDesc": null,
"chartDataModelList": [
{
"x": "20240807",
"y": 0,
"marker": []
}
],
"strategyCount": 1,
"accountPortfolioStrategyOperationInfo": {
"investAmount": 2000000,
"totalProfitAndLossAmount": 0,
"valueAmount": 2000000,
"rate": 0
},
"accountPortfolioDetailInfo": [
{
"portfolioId": 360,
"portfolioName": "정치영님의 포트폴리오 20240807-1603",
"strategyId": "KP0132",
"strategyName": "멀티팩터의 교향곡을 만드는 종목",
"firstDate": "20240807",
"investmentPropensityCode": "3",
"investmentPropensity": "위험중립형",
"strategyTitle": "콴텍 Q-Growth 국내 주식",
"globalType": "1",
"weight": 100,
"itype": "1",
"strategyStatusCode": "A",
"strategyStatusName": "활성화",
"reservationOrderTypeCode": null,
"reservationOrderTypeName": "해지",
"reservationStatusCode": null,
"reservationStatusName": "주문실패",
"weightRate": 1,
"minimumInvestmentAmount": 1500000,
"firstInvestAmount": 2000000,
"addInvestAmount": 0,
"investAmount": 2000000,
"valueAmount": 2000000,
"rate": 0,
"activeStatusCode": "4",
"activeStatusName": "운용중"
}
],
"strategyAssetsRateResultList": null,
"accountStrategyFirstDateCalEtcInfo": null,
"managementPlan": null,
"managementAlgorithmName": null,
"managementAlgorithmRate": null,
"managementAlgorithmUniverse": null,
"managementAlgorithmBenchmark": null,
"managementAlgorithmCoreFector": null,
"managementAlgorithmStyle": null,
"managementAlgorithmRiskManagement": null
}
```
이렇게 조회했을때 firstDate가 있으니까 이 부분을 이용해서
firstDate가 있으니까 이것을 기준으로 TP0015MT의 LAST_WRK_DTM이 앞서면
firstDate < LAST_WRK_DTM 되어 있다면 DB에 있는 약관이 가입한 포트폴리오보다 최신이라는 뜻이 되니까 변경된 약관 동의를 출력
### 업무 2024-08-29

View File

@ -0,0 +1,7 @@
# TEST_하나증권_MTS입점(NEW)
> Q. 하나증권에 계좌가 없는 준회원이다. ci값은 안넘어 올 수 있는데 이경우 콴텍 전략 마켓은 열어주기로 했다는데 해당 메시지는 화면에서 표시되는건가요? 아니면 백엔드 api?
>
> W. 준회원의 의미는 하나증권MTS 회원인데 계좌가 없다는 뜻?
>
>

View File

@ -0,0 +1,37 @@
## PBP-CS 로그인
#### 1.TaskProcess
>
```mermaid
classDiagram
class Browser{
}
class Login{
sccofnstcd: url param;
}
class LoginTask{
counselSeq?: number;
onConfirm?(bool: boolean): void; // 로그 인 완료
isTest?: boolean;
isMTS?: boolean;
}
class PinNumberLoginTask{
onConfirm(): void;
onLoginFail(bool: boolean, idx: number): void;
isMTS: boolean;
}
class ReSignupProcess{
}
class Assets{
}
Browser --> Login : "/login?ref=&sccofnstcd=270"
Login --> LoginTask
LoginTask --> Assets :onConfirm={onConfirmLogin}
LoginTask --> PinNumberLoginTask
LoginTask <-- PinNumberLoginTask
```

View File

@ -0,0 +1,7 @@
## PBP-CS 샘플데이터
PBP-CS는 현재 하나증권 데이터를 받아서 테스트를 하고 있기 때문에 테스트에 어려움 local, dev에서 각 페이지를 통과할 수 있는 방법을 강구하자는 바임
### 1. 로그인
>
> 로그인시 매직키패드 및

View File

@ -437,3 +437,5 @@
4. 기획팀에서 전달 받은 피드백 ppt파일
[기획 수정 요청 ppt 파일](<240821_PB플랫폼 수정요청_리포트.pptx>)
>> 2024-08-28 우선 데이터 맞추어 놨고 디자인은 차후 수정하겠음

View File

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 120 KiB

View File

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 87 KiB

View File

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB