Skip to content

Commit 35c5c54

Browse files
authored
[Refactor] UserRepository 필드 수정 (#25)
## 작업 내역 (관련 이슈) - UserRepository 필드 수정 ## 특이 사항 - UserRepository 필드 수정
2 parents 19032c6 + 4fca860 commit 35c5c54

11 files changed

Lines changed: 735 additions & 7 deletions

File tree

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"devon-aide.resourcePath": "c:\\Users\\user\\.vscode\\extensions\\lgcns-devon-aide.devon-aide-0.0.1\\resource"
3+
}

poetry.lock

Lines changed: 309 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/main/nft/nft_Info_dto.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# from pydantic import BaseModel
2+
# from typing import Dict
3+
# from datetime import datetime
4+
5+
# # 필요한 필드들(Response)
6+
# # 1. user-wallet id
7+
# # 2. nft-id
8+
# # 3. nft-grade
9+
# # 4. transaction_hash #nft 발급 트ㅐㄴ잭션이 성공적으로 기록 되어있는지 확인 가능한 정보
10+
# #(블록체인 익스플로어 에서 트ㅐㄴ잭션을 조회 가능함)
11+
# # 5. nft 메타 데이터
12+
# # 6. 발급일, 만료일
13+
# class NftResponseDTO(BaseModel):
14+
# user_wallet_id: str
15+
# point: int
16+
# nft_id: str
17+
# nft_grade: str
18+
# transaction_hash: str
19+
# nft_metadata_uri: str
20+
# issued_at: datetime
21+
# expired_at: datetime
22+
23+
24+
# # db 저자에 필요한 필드
25+
# # 1. nft-id
26+
# # 2. nft-grade
27+
# # 3. transacation_hash
28+
# # 4. meta_uri
29+
# # nft의 상세 정보(이미지, 설명, 속성 등) 외부 저장소(s3)에 보관
30+
# # 5. 발급, 만료일
31+
# class NftSaveDTO(BaseModel):
32+
# id: str
33+
# user_wallet: str
34+
# nft_id: str
35+
# nft_grade: str
36+
# transaction_hash: str
37+
# nft_metadata_uri: str
38+
# issued_at: datetime
39+
# expired_at: datetime
40+

src/main/nft/nft_controller.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# # controller/nft_controller.py
2+
3+
# from fastapi import APIRouter, Depends, HTTPException
4+
# from sqlalchemy.orm import Session
5+
# from typing import List
6+
7+
# #from database import get_db
8+
# #from nft_
9+
# from src.main.nft.nft_issue_service import process_nft_issuance_with_response
10+
# from src.main.nft.nft_Info_dto import NftResponseDTO
11+
12+
# router = APIRouter(
13+
# prefix="/nft",
14+
# tags=["NFT 발급"]
15+
# )
16+
17+
# @router.post("/issue", response_model=List[NftResponseDTO])
18+
# async def issue_nfts():
19+
# try:
20+
# results = await process_nft_issuance_with_response()
21+
# #results = await test_process_nft_issuance_without_db()
22+
# return results
23+
# except Exception as e:
24+
# raise HTTPException(status_code=500, detail=f"NFT 발급 중 오류 발생: {str(e)}")

src/main/nft/nft_issue_service.py

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
# #1. 포인트를 기준으로 사용자 나열하기
2+
# # 조건) 500포인트 이상인 사람만 걸러서 나열할 것
3+
4+
# #2. 포인트에 따른 nft 등급 매핑
5+
6+
# #3.nft 발급 대상자 선정
7+
# # 1) 전체 상위 3퍼센트 + 1500포가 넘는지 checking(저장) -> nft 발급
8+
# # 2) 전체 상위 10퍼 + 상위 3퍼센트 제외 + 1000포가 넘는지 checking(저장) -> nft 발급급
9+
# # 3) 전체 상위 40퍼 + 상위 3 + 상위 10 제외 + 500포 이상 chekcing(저장) -> ntt 발급
10+
11+
# #4. nft 발급 구현(병렬 처리)
12+
# #5. db에 저장
13+
# #6. return 값은 void로 하는데 checking 용으로 해보기
14+
# # service/nft_service.py
15+
16+
# from src.main.nft.nft_model import NFTRecord
17+
# from src.main.nft.nft_Info_dto import NftResponseDTO
18+
# from src.main.users.repository.UserRepository import UserRepository
19+
# from src.main.nft.nft_repository import save_nfts_bulk
20+
# from datetime import datetime, timezone, timedelta
21+
# import asyncio
22+
23+
# # XRPL 관련 모듈
24+
# #pip install xrpl-py + python -m poetry add xrpl.py
25+
# from xrpl.asyncio.transaction import submit_and_wait
26+
# from xrpl.models.transactions.nftoken_mint import NFTokenMint, NFTokenMintFlag
27+
# from xrpl.asyncio.wallet import generate_faucet_wallet
28+
# from xrpl.wallet import Wallet
29+
# from xrpl.clients import JsonRpcClient
30+
# import json
31+
32+
# # service 내부에서 임시 유저 생성
33+
# # 예시 User 클래스 (있다고 가정)
34+
# # class User:
35+
# # def __init__(self, wallet, point):
36+
# # self.wallet = wallet
37+
# # self.point = point
38+
39+
# # # 개별 사용자 생성
40+
# # user1 = User(wallet="rWallet1", point=1800)
41+
# # user2 = User(wallet="rWallet2", point=1300)
42+
# # user3 = User(wallet="rWallet3", point=800)
43+
44+
# # # 리스트로 묶어서 사용
45+
# # users = [user1, user2, user3]
46+
47+
# repo = UserRepository()
48+
# users = repo.get_all_user()
49+
50+
51+
# # XRPL 설정
52+
# print("Connecting to Testnet...")
53+
# JSON_RPC_URL = "https://s.devnet.rippletest.net:51234/"
54+
# client = JsonRpcClient(JSON_RPC_URL)
55+
56+
57+
# #등급 조건건
58+
# GRADE_RULES = [
59+
# {"grade": "platinum", "percent": 0.03, "min_point": 1500},
60+
# {"grade": "gold", "percent": 0.10, "min_point": 1000},
61+
# {"grade": "silver", "percent": 0.40, "min_point": 500},
62+
# {"grade": "bronze", "percent": 1, "min_point": 0}
63+
# ]
64+
65+
# # 등급별 Taxon 값 설정 (NFT 분류 번호)
66+
# NFT_GRADE_TAXON = {
67+
# "platinum": 4,
68+
# "gold": 3,
69+
# "silver": 2,
70+
# "bronze": 1 # 기본값
71+
# }
72+
73+
# #metadataUri
74+
# NFT_METADATA_URI = {
75+
# "platinum" : "일단 플래티넘 주소",
76+
# "gold": "일단 골드 주소",
77+
# "silver" : "일단 실버 주소",
78+
# "bronze": "일단 브론즈즈 주소"
79+
# }
80+
81+
# # 테스트 지갑 생성 (테스트넷용)
82+
# # generate~ 함수는 내부적으로 비동기 함수임(asyncio.run()) -> ㅇ미 비동기에서 비동기로 겹침침
83+
# async def generate_wallet ():
84+
# wallet = await generate_faucet_wallet(client=client)
85+
# return wallet, wallet.address
86+
87+
# # #포인트별로 필터링 함수
88+
# # def filter_users_by_rank_and_point(users, start_idx, end_idx, min_point):
89+
# # return [
90+
# # #전체 사용자 중
91+
# # # 특정 범위의 사용자 중 최소 포인트 이상인 사람만 필터링해서 반환환
92+
# # user
93+
# # for i, user in enumerate(users[start_idx:end_idx])
94+
# # if user.point >= min_point
95+
# # ]
96+
97+
# # XRPL 기반 NFT 민팅(XRPL 에서 NFT를 실제로 발급(MINt) 하는 핵심 함수)
98+
# # 개별 사용자 1명에게 NFT를 발급하는 함수
99+
# async def mint_nft_on_xrpl(user, grade, issuser_wallet, issuserAddr):
100+
# # 발급일 = 현재 날짜 + 자정
101+
# issued_at = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
102+
# # 만료일 = 6개월 자정
103+
# expired_at = issued_at + timedelta(days=180)
104+
105+
# # now = datetime.utcnow()
106+
# # NFT 발급을 위한 트랜잭션 객체 생성
107+
# # 트랜잭션 : 블록체인에서 일어나는 모든 행동을 기록한 데이터 조각
108+
# # XRPL노드로 전송되면 블록체인에 기록 되고 실제 NFT가 생성됨됨
109+
# # ex) A가 B에게 10코인을 보냄 등.,,
110+
# mint_tx = NFTokenMint(
111+
# account=issuserAddr, # 발급자(대표자)
112+
# nftoken_taxon=NFT_GRADE_TAXON[grade], # NFT 분류 Id (의미를 부여) grade에 따라 자동 적용용
113+
# flags=NFTokenMintFlag.TF_TRANSFERABLE,# NFT 가 전송 가능한 것인지 여부 설정정
114+
# uri=NFT_METADATA_URI[grade].encode("utf-8").hex() # NFT의 이미지, 무슨 등급, 이름/설명/속성 등을 설명해주는 정보 묶음
115+
# )
116+
117+
# try:
118+
# # xrpl에서 해당 정보를 트랜잭션에 넘기기기
119+
# # submit_and_wait(transaction, client(노드 클라이언트 자체), wallet(서명에 사용될 지갑 객체체))
120+
# response = await submit_and_wait(transaction=mint_tx, client=client, wallet=issuser_wallet)
121+
# # transaction 처리 결과
122+
# result = response.result
123+
124+
# #print("result" + result)
125+
126+
# # 트랜잭션 해시 추출(트랜잭션 고유 ID) -> 블록 탐색기에서 이 해시로 NFT 상태 조회 가능
127+
# tx_hash = result['hash']
128+
# if not tx_hash or not isinstance(tx_hash, str):
129+
# raise Exception("트랜잭션 해시를 정상적으로 받지 못했습니다.")
130+
131+
# # NFT ID 파싱을 위함
132+
# nft_id = ""
133+
134+
135+
# #print("트랜잭션 결과:\n", json.dumps(result, indent=2))
136+
137+
138+
# # XRPL 은 발급된 NFT의 ID를 직접 반환 x
139+
# # -> AffectedNodes에서 새로 생성된 노드를 찾아서 안의 ID를 추출해야함함
140+
# #for node in result['meta']['AffectedNodes']:
141+
# # if "CreatedNode" in list(node.keys())[0]:
142+
# # created = node['CreatedNode']['NewFields']
143+
# # if "NFToken" in created.get("NFTokens", [{}])[0]:
144+
# # nft_id = created["NFTokens"][0]["NFToken"]["NFTokenID"]
145+
# # break
146+
147+
# for node in result['meta']['AffectedNodes']:
148+
# node_data = node.get("CreatedNode") or node.get("ModifiedNode")
149+
# if node_data and node_data["LedgerEntryType"] == "NFTokenPage":
150+
# tokens = node_data.get("NewFields", {}).get("NFTokens") or node_data.get("FinalFields", {}).get("NFTokens")
151+
# if tokens:
152+
# for token in tokens:
153+
# nft = token.get("NFToken")
154+
# if nft and "NFTokenID" in nft:
155+
# nft_id = nft["NFTokenID"]
156+
# break
157+
# if nft_id:
158+
# break
159+
160+
161+
162+
# if not nft_id:
163+
# raise Exception("NFT ID 추출 실패")
164+
165+
# return {
166+
# "user_wallet": user.wallet,
167+
# "point": user.point,
168+
# "nft_id": nft_id,
169+
# "nft_grade": grade,
170+
# "transaction_hash": tx_hash,
171+
# "nft_metadata_uri": NFT_METADATA_URI[grade].encode("utf-8").hex(),
172+
# "issued_at": issued_at,
173+
# "expired_at": expired_at
174+
# }
175+
176+
# except Exception as e:
177+
# print(f"NFT 민팅 실패: {e}")
178+
# return None
179+
180+
# # 등급별 민팅(여러 사용자에게 등급별로 NFT를 발급해주는 함수)
181+
# async def mint_all_nfts(users, issuser_wallet, issuserAddr):
182+
# total = len(users) # user가 몇명인지지
183+
# used_indices = 0 # 등급별로 인덱스 나누기 위한 변수
184+
# results = [] # 민팅 결과 담는 곳곳
185+
186+
# # 등급 조건 별로 for 문 돌리기기
187+
# for rule in GRADE_RULES:
188+
# # 퍼센트가 몇 등인지를 계산
189+
# size = max(1, int(total * rule["percent"]))
190+
# # 후보자 필터링(상위 인덱스 구간 내에서 최소 포인트 이상인 유저만)
191+
# # fitler_user_by_rank_and_point(user, start_idx, end_idx, point)
192+
# # candidates = filter_users_by_rank_and_point(
193+
# # users, used_indices, min(used_indices + size, total), rule["min_point"]
194+
# # )
195+
# # 다음 등급의 start_idx 설정정
196+
# # used_indices += size
197+
198+
# # 후보자 리스트에서 nft 발급 준비
199+
# # mint_nft_on_xrpl(user, grade)
200+
# # tasks = []
201+
# # for user in candidates :
202+
# # task = mint_nft_on_xrpl(user, rule["grade"])
203+
# # tasks.append(task)
204+
# # tasks = [mint_nft_on_xrpl(user, rule["grade"], issuser_wallet, issuserAddr) for user in candidates]
205+
# tasks = [mint_nft_on_xrpl(user, rule["grade"], issuser_wallet, issuserAddr) for user in users]
206+
# # nft 발급 결과를 비동기식으로 minted에 저장(minted)
207+
# minted = await asyncio.gather(*tasks) # 위 모든 동작을 동시에 실행행
208+
209+
# # 발급 실패한 사람 제외 results 에 저장해두기
210+
# # extend([저장된 발급 결과에 None 이라 저장되어있는 것])
211+
# results.extend([r for r in minted if r is not None])
212+
213+
# return results
214+
215+
# # 전체 로직
216+
217+
# async def process_nft_issuance_with_response() -> list[NftResponseDTO]:
218+
# issuser_wallet, issuserAddr = await generate_wallet()
219+
220+
# # 포인트 별로 내림차순순
221+
# users.sort(key=lambda u: u.point, reverse=True)
222+
223+
# # 모든 사용자 nft 발급 요청하기
224+
# mint_results = await mint_all_nfts(users, issuser_wallet, issuserAddr)
225+
226+
# # DB 저장용 객체 변환
227+
# nft_records = [
228+
# NFTRecord(
229+
# nft_id=result["nft_id"],
230+
# user_wallet=result["user_wallet"],
231+
# nft_grade=result["nft_grade"],
232+
# transaction_hash=result["transaction_hash"],
233+
# metadata_uri=result["nft_metadata_uri"],
234+
# issued_at=result["issued_at"],
235+
# expires_at=result["expired_at"]
236+
# ) for result in mint_results
237+
# ]
238+
# save_nfts_bulk(nft_records)
239+
240+
# # DTO 변환
241+
# return [
242+
# NftResponseDTO(
243+
# user_wallet_id=r["user_wallet"],
244+
# point=r["point"],
245+
# nft_id=r["nft_id"],
246+
# nft_grade=r["nft_grade"],
247+
# transaction_hash=r["transaction_hash"],
248+
# nft_metadata_uri=r["nft_metadata_uri"],
249+
# issued_at=r["issued_at"],
250+
# expired_at=r["expired_at"]
251+
# ) for r in mint_results
252+
# ]
253+
254+
255+
# # async def test_process_nft_issuance_without_db() -> list[NftResponseDTO]:
256+
# # issuser_wallet, issuserAddr = await generate_wallet()
257+
258+
# # # 포인트 별로 내림차순순
259+
# # users.sort(key=lambda u: u.point, reverse=True)
260+
# # # 모든 사용자 nft 발급 요청하기
261+
# # mint_results = await mint_all_nfts(users, issuser_wallet, issuserAddr)
262+
263+
# # return [
264+
# # NftResponseDTO(
265+
# # user_wallet_id=r["user_wallet"],
266+
# # point=r["point"],
267+
# # nft_id=r["nft_id"],
268+
# # nft_grade=r["nft_grade"],
269+
# # transaction_hash=r["transaction_hash"],
270+
# # nft_metadata_uri=r["nft_metadata_uri"],
271+
# # issued_at=r["issued_at"],
272+
# # expired_at=r["expired_at"]
273+
# # )
274+
# # for r in mint_results
275+
# # ]
276+

src/main/nft/nft_model.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# ##entity 같은 역할입니다.
2+
# from sqlalchemy import Column, String, Integer, DateTime
3+
# from sqlalchemy.ext.declarative import declarative_base
4+
# import datetime
5+
6+
# Base = declarative_base()
7+
8+
# class NFTRecord(Base):
9+
# __tablename__ = "nft_records"
10+
11+
# id = Column(Integer, primary_key=True, autoincrement=True)
12+
# user_wallet = Column(String, index=True, nullable=False)
13+
# nft_id = Column(String, unique=True, nullable=False)
14+
# nft_grade = Column(String, nullable=False)
15+
# transaction_hash = Column(String, unique=True, nullable=False)
16+
# nft_metadata_uri = Column(String, nullable=True)
17+
# issued_at = Column(DateTime, nullable=False)
18+
# expires_at = Column(DateTime, nullable=False)

src/main/nft/nft_repository.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# from src.main.config.mongodb import get_mongo_client
2+
# from src.main.nft.nft_model import NFTRecord
3+
# from datetime import datetime
4+
5+
# # repository/nft_repository.py
6+
# def save_nfts_bulk(nft_records: list):
7+
# client = get_mongo_client()
8+
# db = client['xrpedia-data']
9+
# nft_collection = db['nft_records']
10+
11+
# docs = [
12+
# {
13+
# "nft_id": r.nft_id,
14+
# "user_wallet": r.user_wallet,
15+
# "nft_grade": r.nft_grade,
16+
# "transaction_hash": r.transaction_hash,
17+
# "metadata_uri": r.metadata_uri,
18+
# "issued_at": r.issued_at,
19+
# "expires_at": r.expires_at,
20+
# }
21+
# for r in nft_records
22+
# ]
23+
24+
# if docs:
25+
# nft_collection.insert_many(docs)

0 commit comments

Comments
 (0)