ZHIWEI666 commited on
Commit
dfcb843
·
verified ·
1 Parent(s): d1782de

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +11 -0
  2. main.py +262 -0
  3. requirements.txt +6 -0
Dockerfile ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ WORKDIR /code
4
+
5
+ COPY ./requirements.txt /code/requirements.txt
6
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
7
+
8
+ COPY . .
9
+
10
+ # Hugging Face Spaces 默认将应用暴露在 7860 端口
11
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
main.py ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import uuid
4
+ import requests
5
+ import threading
6
+ from datetime import datetime, timezone
7
+ from fastapi import FastAPI, HTTPException
8
+ from pydantic import BaseModel
9
+ from huggingface_hub import HfApi, CommitOperationAdd
10
+
11
+ app = FastAPI()
12
+
13
+ # ==========================================
14
+ # ⚙️ 全局配置与安全锁
15
+ # ==========================================
16
+ HF_TOKEN = os.environ.get("HF_TOKEN")
17
+ # 🌟 必须替换为你自己的 Dataset 地址!
18
+ DATASET_REPO_ID = "ZHIWEI666/ComfyUI-Ranking"
19
+
20
+ api = HfApi(token=HF_TOKEN)
21
+ db_lock = threading.Lock() # 全局内存锁:强制所有写请求排队,杜绝并发覆盖
22
+
23
+ # ==========================================
24
+ # 📦 数据请求模型 (Pydantic)
25
+ # ==========================================
26
+ class PublishReq(BaseModel):
27
+ uid: str; author_name: str; type: str; name: str; short_desc: str; detailed_desc: str; target_url: str; price: int
28
+ class RedeemReq(BaseModel):
29
+ uid: str; code: str
30
+ class UnlockReq(BaseModel):
31
+ uid: str; content_id: str
32
+
33
+ # ==========================================
34
+ # 🛠️ 核心工具函数:极速读取与原子提交
35
+ # ==========================================
36
+ def fetch_json(filename: str):
37
+ """通过 Raw URL 极速拉取最新 JSON 数据(绕过本地缓存)"""
38
+ url = f"https://huggingface.co/datasets/{DATASET_REPO_ID}/raw/main/{filename}"
39
+ headers = {"Authorization": f"Bearer {HF_TOKEN}"} if HF_TOKEN else {}
40
+ res = requests.get(url, headers=headers, timeout=10)
41
+
42
+ if res.status_code == 200:
43
+ try:
44
+ return res.json()
45
+ except json.JSONDecodeError:
46
+ pass
47
+
48
+ # 如果文件不存在或损坏,返回初始结构
49
+ if filename in ["users.json", "codes.json"]:
50
+ return {}
51
+ return []
52
+
53
+ def commit_multiple_jsons(commit_message: str, file_dict: dict):
54
+ """
55
+ 高级原子提交:将多个修改后的 dict 直接在内存中转为 bytes,
56
+ 并一次性 Commit 到 Hugging Face,保证数据绝对一致性。
57
+ """
58
+ operations = []
59
+ for filename, data in file_dict.items():
60
+ # 将 Python 字典转为格式化的 JSON 字节流
61
+ json_bytes = json.dumps(data, ensure_ascii=False, indent=2).encode('utf-8')
62
+ operations.append(
63
+ CommitOperationAdd(path_in_repo=filename, path_or_fileobj=json_bytes)
64
+ )
65
+
66
+ # 执行打包提交
67
+ api.create_commit(
68
+ repo_id=DATASET_REPO_ID,
69
+ repo_type="dataset",
70
+ operations=operations,
71
+ commit_message=commit_message
72
+ )
73
+
74
+ # ==========================================
75
+ # 🚀 业务路由:拉取数据
76
+ # ==========================================
77
+ @app.get("/")
78
+ def read_root():
79
+ return {"status": "Running", "message": "Ranking API is online!"}
80
+
81
+ @app.get("/api/rankings")
82
+ def get_rankings():
83
+ """直接返回全量作品列表供前端渲染"""
84
+ try:
85
+ contents = fetch_json("contents.json")
86
+ return {"status": "success", "data": contents}
87
+ except Exception as e:
88
+ raise HTTPException(status_code=500, detail=str(e))
89
+
90
+ # ==========================================
91
+ # 📝 业务路由:发布新作品
92
+ # ==========================================
93
+ @app.post("/api/publish")
94
+ def publish_content(req: PublishReq):
95
+ with db_lock:
96
+ try:
97
+ contents_db = fetch_json("contents.json")
98
+ users_db = fetch_json("users.json")
99
+
100
+ # 初始化发布者资料
101
+ if req.uid not in users_db:
102
+ users_db[req.uid] = {"name": req.author_name, "balance": 0}
103
+
104
+ new_item = {
105
+ "id": f"{req.type}_{uuid.uuid4().hex[:8]}",
106
+ "type": req.type,
107
+ "name": req.name,
108
+ "short_desc": req.short_desc,
109
+ "detailed_desc": req.detailed_desc,
110
+ "author": req.author_name,
111
+ "uid": req.uid,
112
+ "target_url": req.target_url,
113
+ "price": req.price,
114
+ "likes": 0, "favs": 0, "cmts": 0, "downloads": 0,
115
+ "comments": [],
116
+ "publish_time": datetime.now(timezone.utc).isoformat()
117
+ }
118
+
119
+ # 插入到列表最前方
120
+ contents_db.insert(0, new_item)
121
+
122
+ # 提交到云端
123
+ commit_multiple_jsons(
124
+ commit_message=f"Publish new {req.type}: {req.name}",
125
+ file_dict={"contents.json": contents_db, "users.json": users_db}
126
+ )
127
+
128
+ return {"status": "success", "message": "发布成功!", "item": new_item}
129
+ except Exception as e:
130
+ print(f"Publish Error: {e}")
131
+ raise HTTPException(status_code=500, detail="云端写入失败")
132
+
133
+ # ==========================================
134
+ # 💰 业务路由:卡密核销充值
135
+ # ==========================================
136
+ @app.post("/api/wallet/redeem")
137
+ def redeem_code(req: RedeemReq):
138
+ code_str = req.code.strip().upper()
139
+ with db_lock:
140
+ try:
141
+ codes_db = fetch_json("codes.json")
142
+ users_db = fetch_json("users.json")
143
+ tx_db = fetch_json("transactions.json")
144
+
145
+ # 1. 校验卡密有效性
146
+ if code_str not in codes_db:
147
+ return {"status": "error", "message": "无效的卡密,请检查是否输入正确。"}
148
+ if codes_db[code_str].get("status") != "active":
149
+ return {"status": "error", "message": "该卡密已被使用或已失效。"}
150
+
151
+ # 2. 初始化用户
152
+ if req.uid not in users_db:
153
+ users_db[req.uid] = {"name": f"User_{req.uid[:4]}", "balance": 0}
154
+
155
+ value = codes_db[code_str]["value"]
156
+ current_time = datetime.now(timezone.utc).isoformat()
157
+
158
+ # 3. 更新状态 (卡密作废,增加余额)
159
+ codes_db[code_str]["status"] = "used"
160
+ codes_db[code_str]["used_by_uid"] = req.uid
161
+ codes_db[code_str]["used_at"] = current_time
162
+
163
+ users_db[req.uid]["balance"] += value
164
+ new_balance = users_db[req.uid]["balance"]
165
+
166
+ # 4. 记录流水
167
+ tx_record = {
168
+ "tx_id": f"TX-RCG-{uuid.uuid4().hex[:6].upper()}",
169
+ "uid": req.uid, "type": "recharge", "amount": value,
170
+ "related_id": code_str, "timestamp": current_time
171
+ }
172
+ tx_db.append(tx_record)
173
+
174
+ # 5. 原子提交 3 个文件
175
+ commit_multiple_jsons(
176
+ commit_message=f"User {req.uid} redeemed code {code_str}",
177
+ file_dict={
178
+ "codes.json": codes_db,
179
+ "users.json": users_db,
180
+ "transactions.json": tx_db
181
+ }
182
+ )
183
+
184
+ return {"status": "success", "message": f"成功充值 {value} C币!", "new_balance": new_balance}
185
+ except Exception as e:
186
+ print(f"Redeem Error: {e}")
187
+ raise HTTPException(status_code=500, detail="核销系统异常")
188
+
189
+ # ==========================================
190
+ # 🔓 业务路由:扣费与资产解锁
191
+ # ==========================================
192
+ @app.post("/api/unlock")
193
+ def unlock_content(req: UnlockReq):
194
+ with db_lock:
195
+ try:
196
+ contents_db = fetch_json("contents.json")
197
+ users_db = fetch_json("users.json")
198
+ orders_db = fetch_json("orders.json")
199
+ tx_db = fetch_json("transactions.json")
200
+
201
+ buyer_uid = req.uid
202
+ content = next((item for item in contents_db if item["id"] == req.content_id), None)
203
+ if not content:
204
+ return {"status": "error", "message": "未找到该作品"}
205
+
206
+ price = content.get("price", 0)
207
+ seller_uid = content.get("uid", "admin")
208
+
209
+ # 1. 免费直接下发
210
+ if price == 0:
211
+ return {"status": "success", "message": "获取成功", "target_url": content["target_url"], "type": content["type"]}
212
+
213
+ # 2. 幂等性校验 (已经买过或自己买自己的,直接放行,不扣费)
214
+ already_bought = any(o["buyer_uid"] == buyer_uid and o["content_id"] == req.content_id for o in orders_db)
215
+ if already_bought or buyer_uid == seller_uid:
216
+ return {"status": "success", "message": "已恢复购买记录", "target_url": content["target_url"], "type": content["type"], "new_balance": users_db.get(buyer_uid, {}).get("balance", 0)}
217
+
218
+ # 3. 校验买家余额
219
+ buyer_data = users_db.get(buyer_uid)
220
+ if not buyer_data or buyer_data.get("balance", 0) < price:
221
+ return {"status": "error", "message": "可用余额不足,请先充值"}
222
+
223
+ # 4. 执行扣费事务
224
+ # 4.1 资金转移
225
+ users_db[buyer_uid]["balance"] -= price
226
+ if seller_uid in users_db:
227
+ users_db[seller_uid]["balance"] += price
228
+
229
+ # 4.2 增加作品下载量
230
+ content["downloads"] = content.get("downloads", 0) + 1
231
+
232
+ # 4.3 生成订单
233
+ current_time = datetime.now(timezone.utc).isoformat()
234
+ order_id = f"ORD-{uuid.uuid4().hex[:8].upper()}"
235
+ orders_db.append({"order_id": order_id, "buyer_uid": buyer_uid, "seller_uid": seller_uid, "content_id": req.content_id, "price": price, "timestamp": current_time})
236
+
237
+ # 4.4 生成买卖双方流水
238
+ tx_db.append({"tx_id": f"TX-BUY-{uuid.uuid4().hex[:6]}", "uid": buyer_uid, "type": "consume", "amount": -price, "related_id": order_id, "timestamp": current_time})
239
+ tx_db.append({"tx_id": f"TX-SEL-{uuid.uuid4().hex[:6]}", "uid": seller_uid, "type": "earn", "amount": price, "related_id": order_id, "timestamp": current_time})
240
+
241
+ # 5. 原子提交 4 个受影响的文件
242
+ commit_multiple_jsons(
243
+ commit_message=f"Unlock {req.content_id} by {buyer_uid}",
244
+ file_dict={
245
+ "users.json": users_db,
246
+ "orders.json": orders_db,
247
+ "transactions.json": tx_db,
248
+ "contents.json": contents_db # 下载量+1了,所以要更新总表
249
+ }
250
+ )
251
+
252
+ return {
253
+ "status": "success",
254
+ "message": "支付并解锁成功!",
255
+ "target_url": content["target_url"],
256
+ "type": content["type"],
257
+ "new_balance": users_db[buyer_uid]["balance"]
258
+ }
259
+
260
+ except Exception as e:
261
+ print(f"Unlock Error: {e}")
262
+ raise HTTPException(status_code=500, detail="交易系统异常")
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ huggingface_hub
4
+ pydantic
5
+ requests
6
+ filelock