ZHIWEI666 commited on
Commit
cfb2ea0
·
verified ·
1 Parent(s): 552acde

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +99 -161
app.py CHANGED
@@ -7,12 +7,10 @@ import time
7
  import uuid
8
  import hashlib
9
 
10
- # 导入数据库操作模块
11
  import 数据库连接 as db
12
 
13
  app = FastAPI(title="ComfyUI Ranking Community API")
14
 
15
- # 允许跨域请求,这是 ComfyUI 能够访问云端的关键
16
  app.add_middleware(
17
  CORSMiddleware,
18
  allow_origins=["*"],
@@ -21,52 +19,36 @@ app.add_middleware(
21
  allow_headers=["*"],
22
  )
23
 
24
- # --- 增加根目录探针,防止 Hugging Face 健康检查失败 ---
25
  @app.get("/")
26
  def read_root():
27
  return {"status": "ok", "message": "ComfyUI Ranking API is running!"}
28
 
29
- # --- 【新增】:通用文件上传与哈希分类接口 ---
30
  @app.post("/api/upload")
31
  async def upload_file(file: UploadFile = File(...), file_type: str = Form(...)):
32
  content = await file.read()
33
-
34
- # 计算文件内容的 MD5 哈希值,截取前10位保证唯一性
35
  file_hash = hashlib.md5(content).hexdigest()[:10]
36
  original_name = file.filename
37
- # 将哈希值拼接到文件名前,防止同名文件覆盖 (后台物理名称)
38
  new_filename = f"{file_hash}_{original_name}"
39
 
40
- # 根据文件类型,物理隔离到不同的目录
41
- dir_mapping = {
42
- "avatar": "avatars",
43
- "cover": "covers",
44
- "tool": "tools",
45
- "app": "apps"
46
- }
47
  target_dir = dir_mapping.get(file_type, "others")
48
  full_path_in_repo = f"{target_dir}/{new_filename}"
49
 
50
- # 将真实文件存入数据库(Dataset)
51
  db.save_file(full_path_in_repo, content)
52
-
53
- # 组装供前端直接下载或显示的原始直连 URL
54
  url = f"https://huggingface.co/datasets/{db.DATASET_REPO_ID}/resolve/main/{full_path_in_repo}"
55
 
56
  return {
57
- "status": "success",
58
- "url": url, # 前端 src 或 href 使用的真实链接
59
- "display_name": original_name, # 供前端 UI 干净展示的文件名
60
- "hashed_name": new_filename # 实际带哈希的文件名
61
  }
62
 
63
  # --- 数据模型 ---
64
  class UserRegister(BaseModel):
65
- account: str # 唯一账号 (新增为主键)
66
- password: str # 密码 (新增)
67
- email: str # 邮箱 (新增)
68
- phone: str # 手机号 (新增)
69
- name: str # 显示名称 (昵称)
70
  gender: str
71
  avatarDataUrl: Optional[str] = None
72
  age: Optional[int] = None
@@ -75,23 +57,37 @@ class UserRegister(BaseModel):
75
  intro: Optional[str] = None
76
 
77
  class UserLogin(BaseModel):
78
- account: str # 改为使用 account 登录
79
  password: str
80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  class InteractionToggle(BaseModel):
82
  item_id: str
83
- user_id: str # 使用 account 作为唯一标识
84
- action_type: str # "like", "favorite"
85
  is_active: bool
86
 
87
  class CommentCreate(BaseModel):
88
  item_id: str
89
- author: str # 存储用户的 account 或 name
90
  content: str
91
  reply_to_user: Optional[str] = None
92
  parent_id: Optional[str] = None
93
 
94
- # 【核心修改】:新增发布项目的数据模型
95
  class ItemCreate(BaseModel):
96
  type: str
97
  title: str
@@ -100,9 +96,8 @@ class ItemCreate(BaseModel):
100
  link: str
101
  coverUrl: Optional[str] = None
102
  author: str
103
- price: float = 0.0 # 标价/积分字段
104
 
105
- # 【新增】:关注功能的数据模型
106
  class FollowToggle(BaseModel):
107
  user_id: str
108
  target_account: str
@@ -113,62 +108,43 @@ class FollowToggle(BaseModel):
113
  async def register_user(user: UserRegister):
114
  users_db = db.load_data("users.json", default_data={})
115
 
116
- # 通过 account 查重
117
- if user.account in users_db:
118
- raise HTTPException(status_code=400, detail="该账号已被注册,请尝试其他账号名")
119
-
120
- # 额外防重校验:邮箱和手机号不能被多个账号绑定
121
  for existing_user in users_db.values():
122
- if existing_user.get("email") == user.email:
123
- raise HTTPException(status_code=400, detail="该邮箱已被绑定")
124
- if existing_user.get("phone") == user.phone:
125
- raise HTTPException(status_code=400, detail="���手机号已被绑定")
126
 
127
- # 构建用户档案,初始化各项统计为 0
128
  new_user = user.dict()
129
  new_user.update({
130
  "created_at": int(time.time()),
131
- "followers": [],
132
- "following": [],
133
  "privacy": {"follows": False, "likes": False, "favorites": False, "downloads": False}
134
  })
135
 
136
- users_db[user.account] = new_user # 使用 account 作为字典的 Key
137
  db.save_data("users.json", users_db)
138
 
139
- # 返回时为了安全,剔除密码字段
140
  safe_user_data = {k: v for k, v in new_user.items() if k != "password"}
141
  return {"status": "success", "message": "注册成功", "data": safe_user_data}
142
 
143
  @app.post("/api/users/login")
144
  async def login_user(user: UserLogin):
145
  users_db = db.load_data("users.json", default_data={})
146
-
147
- if user.account not in users_db:
148
- raise HTTPException(status_code=404, detail="账号不存在,请检查或先注册")
149
-
150
  user_data = users_db[user.account]
151
-
152
- if user_data.get("password") != user.password:
153
- raise HTTPException(status_code=401, detail="密码错误")
154
 
155
  return {
156
- "status": "success",
157
- "token": f"mock_token_{user.account}", # 模拟生成 token
158
- "account": user.account,
159
- "name": user_data["name"],
160
  "avatar": user_data.get("avatarDataUrl", "https://via.placeholder.com/150")
161
  }
162
 
163
  @app.get("/api/users/{account}")
164
  async def get_user_profile(account: str):
165
  users_db = db.load_data("users.json", default_data={})
166
- if account not in users_db:
167
- raise HTTPException(status_code=404, detail="用户不存在")
168
 
169
  user_data = users_db[account]
170
-
171
- # 【核心】:动态聚合该用户的作品收到的点赞、收藏和被使用总数
172
  items_db = db.load_data("items.json", default_data=[])
173
  user_items = [item for item in items_db if item.get("author") == account]
174
 
@@ -179,11 +155,41 @@ async def get_user_profile(account: str):
179
  safe_user_data = {k: v for k, v in user_data.items() if k != "password"}
180
  return {"status": "success", "data": safe_user_data}
181
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  @app.post("/api/users/follow")
183
  async def toggle_follow(follow: FollowToggle):
184
  users_db = db.load_data("users.json", default_data={})
185
- if follow.target_account not in users_db or follow.user_id not in users_db:
186
- raise HTTPException(status_code=404, detail="用户不存在")
187
 
188
  target_user = users_db[follow.target_account]
189
  current_user = users_db[follow.user_id]
@@ -201,25 +207,17 @@ async def toggle_follow(follow: FollowToggle):
201
  db.save_data("users.json", users_db)
202
  return {"status": "success"}
203
 
204
- # --- 2. 排行榜核心数据流 ---
205
  @app.get("/api/items")
206
  async def get_items(type: str = "tool", sort: str = "time", limit: int = 20):
207
  items_db = db.load_data("items.json", default_data=[])
208
-
209
  filtered_items = [item for item in items_db if item.get("type") == type]
210
-
211
- if sort == "likes":
212
- filtered_items.sort(key=lambda x: x.get("likes", 0), reverse=True)
213
- elif sort == "favorites":
214
- filtered_items.sort(key=lambda x: x.get("favorites", 0), reverse=True)
215
- elif sort == "downloads":
216
- filtered_items.sort(key=lambda x: x.get("uses", 0), reverse=True)
217
- else:
218
- filtered_items.sort(key=lambda x: x.get("created_at", 0), reverse=True)
219
-
220
  return {"status": "success", "data": filtered_items[:limit]}
221
 
222
- # 【新增】:创作者排行榜动态聚合与排序接口
223
  @app.get("/api/creators")
224
  async def get_creators(sort: str = "downloads", limit: int = 20):
225
  users_db = db.load_data("users.json", default_data={})
@@ -235,18 +233,10 @@ async def get_creators(sort: str = "downloads", limit: int = 20):
235
  apps_count = sum(1 for i in u_items if i.get("type") == "app")
236
 
237
  creators.append({
238
- "account": account,
239
- "name": u.get("name", account),
240
- "avatar": u.get("avatarDataUrl", "https://via.placeholder.com/150"),
241
- "shortDesc": u.get("intro", "这个人很懒,什么都没写..."),
242
- "fullDesc": u.get("intro", "这个人很懒,什么都没写..."),
243
- "likes": likes,
244
- "favorites": favorites,
245
- "downloads": uses,
246
- "toolsCount": tools_count,
247
- "appsCount": apps_count,
248
- "followers": len(u.get("followers", [])),
249
- "created_at": u.get("created_at", 0),
250
  "trendData": {"tools": [0,0,0,0,0, tools_count], "apps": [0,0,0,0,0, apps_count]}
251
  })
252
 
@@ -254,105 +244,58 @@ async def get_creators(sort: str = "downloads", limit: int = 20):
254
  elif sort == "favorites": creators.sort(key=lambda x: x.get("favorites", 0), reverse=True)
255
  elif sort == "downloads": creators.sort(key=lambda x: x.get("downloads", 0), reverse=True)
256
  else: creators.sort(key=lambda x: x.get("created_at", 0), reverse=True)
257
-
258
  return {"status": "success", "data": creators[:limit]}
259
 
260
- # 【核心修改】:处理前端发布的新插件/应用请求
261
  @app.post("/api/items")
262
  async def create_item(item: ItemCreate):
263
  items_db = db.load_data("items.json", default_data=[])
264
-
265
- # 生成独一无二的项目 ID (如: tool_169..._a1b2c3)
266
  new_id = f"{item.type}_{int(time.time())}_{uuid.uuid4().hex[:6]}"
267
-
268
  new_item = {
269
- "id": new_id,
270
- "type": item.type,
271
- "title": item.title,
272
- "author": item.author,
273
- "shortDesc": item.shortDesc,
274
- "fullDesc": item.fullDesc,
275
- "link": item.link,
276
- "coverUrl": item.coverUrl,
277
- "price": item.price,
278
- "likes": 0,
279
- "favorites": 0,
280
- "comments": 0,
281
- "uses": 0,
282
- "created_at": int(time.time()),
283
- "liked_by": [],
284
- "favorited_by": []
285
  }
286
-
287
- # 核心:将新项目插入到列表的【最前面】(第0位),保证 "最新发布" 排在第一
288
  items_db.insert(0, new_item)
289
  db.save_data("items.json", items_db)
290
-
291
  return {"status": "success", "data": new_item}
292
 
293
- # --- 3. 社交与互动 (点赞/收藏防抖与排重) ---
294
  @app.post("/api/interactions/toggle")
295
  async def toggle_interaction(interaction: InteractionToggle):
296
  items_db = db.load_data("items.json", default_data=[])
297
-
298
  target_item = next((item for item in items_db if item["id"] == interaction.item_id), None)
299
- if not target_item:
300
- raise HTTPException(status_code=404, detail="目标工具/应用不存在")
301
 
302
  list_key = f"{interaction.action_type}d_by"
303
  count_key = "likes" if interaction.action_type == "like" else "favorites"
304
-
305
- if list_key not in target_item:
306
- target_item[list_key] = []
307
- target_item[count_key] = 0
308
 
309
  user_list = target_item[list_key]
310
-
311
  if interaction.is_active:
312
- if interaction.user_id not in user_list:
313
- user_list.append(interaction.user_id)
314
- target_item[count_key] += 1
315
  else:
316
- if interaction.user_id in user_list:
317
- user_list.remove(interaction.user_id)
318
- target_item[count_key] = max(0, target_item[count_key] - 1)
319
 
320
  db.save_data("items.json", items_db)
321
-
322
- return {
323
- "status": "success",
324
- "message": "操作成功",
325
- "new_count": target_item[count_key]
326
- }
327
 
328
  # --- 4. 评论系统 ---
329
  @app.post("/api/comments")
330
  async def post_comment(comment: CommentCreate):
331
  comments_db = db.load_data("comments.json", default_data={})
332
  item_comments = comments_db.get(comment.item_id, [])
333
-
334
  new_comment = {
335
- "id": f"c_{int(time.time())}_{uuid.uuid4().hex[:6]}",
336
- "author": comment.author,
337
- "content": comment.content,
338
- "replyToUser": comment.reply_to_user,
339
- "isDeleted": False,
340
- "replies": [],
341
- "created_at": int(time.time())
342
  }
343
-
344
  if comment.parent_id:
345
  parent = next((c for c in item_comments if c["id"] == comment.parent_id), None)
346
- if parent:
347
- parent["replies"].append(new_comment)
348
- else:
349
- raise HTTPException(status_code=404, detail="找不到要回复的父级评论")
350
- else:
351
- item_comments.append(new_comment)
352
-
353
  comments_db[comment.item_id] = item_comments
354
  db.save_data("comments.json", comments_db)
355
-
356
  return {"status": "success", "data": new_comment}
357
 
358
  @app.delete("/api/comments/{item_id}/{comment_id}")
@@ -363,17 +306,12 @@ async def soft_delete_comment(item_id: str, comment_id: str, author: str):
363
  def find_and_delete(comments_list):
364
  for c in comments_list:
365
  if c["id"] == comment_id:
366
- if c["author"] != author:
367
- raise HTTPException(status_code=403, detail="无权删除他人的评论")
368
- c["isDeleted"] = True
369
- c["content"] = ""
370
- return True
371
- if "replies" in c and find_and_delete(c["replies"]):
372
- return True
373
  return False
374
 
375
  if find_and_delete(item_comments):
376
  db.save_data("comments.json", comments_db)
377
- return {"status": "success", "message": "评论已删除"}
378
- else:
379
- raise HTTPException(status_code=404, detail="找不到该评论")
 
7
  import uuid
8
  import hashlib
9
 
 
10
  import 数据库连接 as db
11
 
12
  app = FastAPI(title="ComfyUI Ranking Community API")
13
 
 
14
  app.add_middleware(
15
  CORSMiddleware,
16
  allow_origins=["*"],
 
19
  allow_headers=["*"],
20
  )
21
 
 
22
  @app.get("/")
23
  def read_root():
24
  return {"status": "ok", "message": "ComfyUI Ranking API is running!"}
25
 
 
26
  @app.post("/api/upload")
27
  async def upload_file(file: UploadFile = File(...), file_type: str = Form(...)):
28
  content = await file.read()
 
 
29
  file_hash = hashlib.md5(content).hexdigest()[:10]
30
  original_name = file.filename
 
31
  new_filename = f"{file_hash}_{original_name}"
32
 
33
+ dir_mapping = {"avatar": "avatars", "cover": "covers", "tool": "tools", "app": "apps"}
 
 
 
 
 
 
34
  target_dir = dir_mapping.get(file_type, "others")
35
  full_path_in_repo = f"{target_dir}/{new_filename}"
36
 
 
37
  db.save_file(full_path_in_repo, content)
 
 
38
  url = f"https://huggingface.co/datasets/{db.DATASET_REPO_ID}/resolve/main/{full_path_in_repo}"
39
 
40
  return {
41
+ "status": "success", "url": url,
42
+ "display_name": original_name, "hashed_name": new_filename
 
 
43
  }
44
 
45
  # --- 数据模型 ---
46
  class UserRegister(BaseModel):
47
+ account: str
48
+ password: str
49
+ email: str
50
+ phone: str
51
+ name: str
52
  gender: str
53
  avatarDataUrl: Optional[str] = None
54
  age: Optional[int] = None
 
57
  intro: Optional[str] = None
58
 
59
  class UserLogin(BaseModel):
60
+ account: str
61
  password: str
62
 
63
+ # 【新增阶段一】:用户资料更新模型
64
+ class UserUpdate(BaseModel):
65
+ name: Optional[str] = None
66
+ intro: Optional[str] = None
67
+ age: Optional[int] = None
68
+ country: Optional[str] = None
69
+ region: Optional[str] = None
70
+ gender: Optional[str] = None
71
+ avatarDataUrl: Optional[str] = None
72
+
73
+ # 【新增阶段一】:修改密码模型
74
+ class PasswordReset(BaseModel):
75
+ old_password: str
76
+ new_password: str
77
+
78
  class InteractionToggle(BaseModel):
79
  item_id: str
80
+ user_id: str
81
+ action_type: str
82
  is_active: bool
83
 
84
  class CommentCreate(BaseModel):
85
  item_id: str
86
+ author: str
87
  content: str
88
  reply_to_user: Optional[str] = None
89
  parent_id: Optional[str] = None
90
 
 
91
  class ItemCreate(BaseModel):
92
  type: str
93
  title: str
 
96
  link: str
97
  coverUrl: Optional[str] = None
98
  author: str
99
+ price: float = 0.0
100
 
 
101
  class FollowToggle(BaseModel):
102
  user_id: str
103
  target_account: str
 
108
  async def register_user(user: UserRegister):
109
  users_db = db.load_data("users.json", default_data={})
110
 
111
+ if user.account in users_db: raise HTTPException(status_code=400, detail="该账号已被注册")
 
 
 
 
112
  for existing_user in users_db.values():
113
+ if existing_user.get("email") == user.email: raise HTTPException(status_code=400, detail="该邮箱已被绑定")
114
+ if existing_user.get("phone") == user.phone: raise HTTPException(status_code=400, detail="该手机号已被绑定")
 
 
115
 
 
116
  new_user = user.dict()
117
  new_user.update({
118
  "created_at": int(time.time()),
119
+ "followers": [], "following": [],
 
120
  "privacy": {"follows": False, "likes": False, "favorites": False, "downloads": False}
121
  })
122
 
123
+ users_db[user.account] = new_user
124
  db.save_data("users.json", users_db)
125
 
 
126
  safe_user_data = {k: v for k, v in new_user.items() if k != "password"}
127
  return {"status": "success", "message": "注册成功", "data": safe_user_data}
128
 
129
  @app.post("/api/users/login")
130
  async def login_user(user: UserLogin):
131
  users_db = db.load_data("users.json", default_data={})
132
+ if user.account not in users_db: raise HTTPException(status_code=404, detail="账号不存在,请检查或先注册")
 
 
 
133
  user_data = users_db[user.account]
134
+ if user_data.get("password") != user.password: raise HTTPException(status_code=401, detail="密码错误")
 
 
135
 
136
  return {
137
+ "status": "success", "token": f"mock_token_{user.account}",
138
+ "account": user.account, "name": user_data["name"],
 
 
139
  "avatar": user_data.get("avatarDataUrl", "https://via.placeholder.com/150")
140
  }
141
 
142
  @app.get("/api/users/{account}")
143
  async def get_user_profile(account: str):
144
  users_db = db.load_data("users.json", default_data={})
145
+ if account not in users_db: raise HTTPException(status_code=404, detail="用户不存在")
 
146
 
147
  user_data = users_db[account]
 
 
148
  items_db = db.load_data("items.json", default_data=[])
149
  user_items = [item for item in items_db if item.get("author") == account]
150
 
 
155
  safe_user_data = {k: v for k, v in user_data.items() if k != "password"}
156
  return {"status": "success", "data": safe_user_data}
157
 
158
+ # 【新增阶段一】:全量更新用户资料
159
+ @app.put("/api/users/{account}")
160
+ async def update_user_profile(account: str, update_data: UserUpdate):
161
+ users_db = db.load_data("users.json", default_data={})
162
+ if account not in users_db: raise HTTPException(status_code=404, detail="用户不存在")
163
+
164
+ user = users_db[account]
165
+ update_dict = update_data.dict(exclude_unset=True) # 仅更新传入的字段
166
+
167
+ for k, v in update_dict.items():
168
+ if v is not None:
169
+ user[k] = v
170
+
171
+ db.save_data("users.json", users_db)
172
+ safe_user_data = {k: v for k, v in user.items() if k != "password"}
173
+ return {"status": "success", "message": "资料更新成功", "data": safe_user_data}
174
+
175
+ # 【新增阶段一】:修改账号密码
176
+ @app.post("/api/users/{account}/reset-password")
177
+ async def reset_password(account: str, pwd_data: PasswordReset):
178
+ users_db = db.load_data("users.json", default_data={})
179
+ if account not in users_db: raise HTTPException(status_code=404, detail="用户不存在")
180
+
181
+ user = users_db[account]
182
+ if user.get("password") != pwd_data.old_password:
183
+ raise HTTPException(status_code=401, detail="原密码错误,修改失败")
184
+
185
+ user["password"] = pwd_data.new_password
186
+ db.save_data("users.json", users_db)
187
+ return {"status": "success", "message": "密码修改成功"}
188
+
189
  @app.post("/api/users/follow")
190
  async def toggle_follow(follow: FollowToggle):
191
  users_db = db.load_data("users.json", default_data={})
192
+ if follow.target_account not in users_db or follow.user_id not in users_db: raise HTTPException(status_code=404, detail="用户不存在")
 
193
 
194
  target_user = users_db[follow.target_account]
195
  current_user = users_db[follow.user_id]
 
207
  db.save_data("users.json", users_db)
208
  return {"status": "success"}
209
 
210
+ # --- 2. 排行榜与发布 ---
211
  @app.get("/api/items")
212
  async def get_items(type: str = "tool", sort: str = "time", limit: int = 20):
213
  items_db = db.load_data("items.json", default_data=[])
 
214
  filtered_items = [item for item in items_db if item.get("type") == type]
215
+ if sort == "likes": filtered_items.sort(key=lambda x: x.get("likes", 0), reverse=True)
216
+ elif sort == "favorites": filtered_items.sort(key=lambda x: x.get("favorites", 0), reverse=True)
217
+ elif sort == "downloads": filtered_items.sort(key=lambda x: x.get("uses", 0), reverse=True)
218
+ else: filtered_items.sort(key=lambda x: x.get("created_at", 0), reverse=True)
 
 
 
 
 
 
219
  return {"status": "success", "data": filtered_items[:limit]}
220
 
 
221
  @app.get("/api/creators")
222
  async def get_creators(sort: str = "downloads", limit: int = 20):
223
  users_db = db.load_data("users.json", default_data={})
 
233
  apps_count = sum(1 for i in u_items if i.get("type") == "app")
234
 
235
  creators.append({
236
+ "account": account, "name": u.get("name", account), "avatar": u.get("avatarDataUrl", "https://via.placeholder.com/150"),
237
+ "shortDesc": u.get("intro", "这个人很懒,什么都没写..."), "fullDesc": u.get("intro", "这个人很懒,什么都没写..."),
238
+ "likes": likes, "favorites": favorites, "downloads": uses, "toolsCount": tools_count, "appsCount": apps_count,
239
+ "followers": len(u.get("followers", [])), "created_at": u.get("created_at", 0),
 
 
 
 
 
 
 
 
240
  "trendData": {"tools": [0,0,0,0,0, tools_count], "apps": [0,0,0,0,0, apps_count]}
241
  })
242
 
 
244
  elif sort == "favorites": creators.sort(key=lambda x: x.get("favorites", 0), reverse=True)
245
  elif sort == "downloads": creators.sort(key=lambda x: x.get("downloads", 0), reverse=True)
246
  else: creators.sort(key=lambda x: x.get("created_at", 0), reverse=True)
 
247
  return {"status": "success", "data": creators[:limit]}
248
 
 
249
  @app.post("/api/items")
250
  async def create_item(item: ItemCreate):
251
  items_db = db.load_data("items.json", default_data=[])
 
 
252
  new_id = f"{item.type}_{int(time.time())}_{uuid.uuid4().hex[:6]}"
 
253
  new_item = {
254
+ "id": new_id, "type": item.type, "title": item.title, "author": item.author,
255
+ "shortDesc": item.shortDesc, "fullDesc": item.fullDesc, "link": item.link,
256
+ "coverUrl": item.coverUrl, "price": item.price, "likes": 0, "favorites": 0,
257
+ "comments": 0, "uses": 0, "created_at": int(time.time()), "liked_by": [], "favorited_by": []
 
 
 
 
 
 
 
 
 
 
 
 
258
  }
 
 
259
  items_db.insert(0, new_item)
260
  db.save_data("items.json", items_db)
 
261
  return {"status": "success", "data": new_item}
262
 
263
+ # --- 3. 社交与互动 ---
264
  @app.post("/api/interactions/toggle")
265
  async def toggle_interaction(interaction: InteractionToggle):
266
  items_db = db.load_data("items.json", default_data=[])
 
267
  target_item = next((item for item in items_db if item["id"] == interaction.item_id), None)
268
+ if not target_item: raise HTTPException(status_code=404, detail="目标存在问题")
 
269
 
270
  list_key = f"{interaction.action_type}d_by"
271
  count_key = "likes" if interaction.action_type == "like" else "favorites"
272
+ if list_key not in target_item: target_item[list_key] = []; target_item[count_key] = 0
 
 
 
273
 
274
  user_list = target_item[list_key]
 
275
  if interaction.is_active:
276
+ if interaction.user_id not in user_list: user_list.append(interaction.user_id); target_item[count_key] += 1
 
 
277
  else:
278
+ if interaction.user_id in user_list: user_list.remove(interaction.user_id); target_item[count_key] = max(0, target_item[count_key] - 1)
 
 
279
 
280
  db.save_data("items.json", items_db)
281
+ return {"status": "success", "new_count": target_item[count_key]}
 
 
 
 
 
282
 
283
  # --- 4. 评论系统 ---
284
  @app.post("/api/comments")
285
  async def post_comment(comment: CommentCreate):
286
  comments_db = db.load_data("comments.json", default_data={})
287
  item_comments = comments_db.get(comment.item_id, [])
 
288
  new_comment = {
289
+ "id": f"c_{int(time.time())}_{uuid.uuid4().hex[:6]}", "author": comment.author, "content": comment.content,
290
+ "replyToUser": comment.reply_to_user, "isDeleted": False, "replies": [], "created_at": int(time.time())
 
 
 
 
 
291
  }
 
292
  if comment.parent_id:
293
  parent = next((c for c in item_comments if c["id"] == comment.parent_id), None)
294
+ if parent: parent["replies"].append(new_comment)
295
+ else: raise HTTPException(status_code=404, detail="找不到要回复的评论")
296
+ else: item_comments.append(new_comment)
 
 
 
 
297
  comments_db[comment.item_id] = item_comments
298
  db.save_data("comments.json", comments_db)
 
299
  return {"status": "success", "data": new_comment}
300
 
301
  @app.delete("/api/comments/{item_id}/{comment_id}")
 
306
  def find_and_delete(comments_list):
307
  for c in comments_list:
308
  if c["id"] == comment_id:
309
+ if c["author"] != author: raise HTTPException(status_code=403, detail="无权删除他人的评论")
310
+ c["isDeleted"] = True; c["content"] = ""; return True
311
+ if "replies" in c and find_and_delete(c["replies"]): return True
 
 
 
 
312
  return False
313
 
314
  if find_and_delete(item_comments):
315
  db.save_data("comments.json", comments_db)
316
+ return {"status": "success", "message": "评论已删除"}
317
+ raise HTTPException(status_code=404, detail="找不到该评论")