ZHIWEI666 commited on
Commit
5d58797
·
verified ·
1 Parent(s): d6f3c57

优化、排除bug

Browse files
Files changed (2) hide show
  1. notifications.py +30 -26
  2. router_items.py +112 -79
notifications.py CHANGED
@@ -3,29 +3,33 @@ import uuid
3
  import 数据库连接 as db
4
 
5
  def add_notification(target_account: str, notif_data: dict):
6
- from_user = notif_data.get("from_user")
7
- if target_account == from_user or not target_account:
8
- return # 不给自己发通知
9
-
10
- users_db = db.load_data("users.json", default_data={})
11
- user_info = users_db.get(from_user, {})
12
-
13
- notif = {
14
- "id": f"msg_{int(time.time())}_{uuid.uuid4().hex[:6]}",
15
- "type": notif_data.get("type"),
16
- "from_user": from_user,
17
- "from_name": user_info.get("name", from_user),
18
- "from_avatar": user_info.get("avatarDataUrl", ""),
19
- "target_item_id": notif_data.get("target_item_id", ""),
20
- "target_item_title": notif_data.get("target_item_title", ""),
21
- "content": notif_data.get("content", ""),
22
- "is_read": False,
23
- "created_at": int(time.time())
24
- }
25
-
26
- def updater(data):
27
- if target_account not in data:
28
- data[target_account] = []
29
- data[target_account].insert(0, notif)
30
-
31
- db.atomic_update("messages.json", updater, default_data={})
 
 
 
 
 
3
  import 数据库连接 as db
4
 
5
  def add_notification(target_account: str, notif_data: dict):
6
+ try:
7
+ from_user = notif_data.get("from_user")
8
+ if target_account == from_user or not target_account:
9
+ return # 不给自己发通知
10
+
11
+ users_db = db.load_data("users.json", default_data={})
12
+ user_info = users_db.get(from_user, {})
13
+
14
+ notif = {
15
+ "id": f"msg_{int(time.time())}_{uuid.uuid4().hex[:6]}",
16
+ "type": notif_data.get("type"),
17
+ "from_user": from_user,
18
+ "from_name": user_info.get("name", from_user),
19
+ "from_avatar": user_info.get("avatarDataUrl", ""),
20
+ "target_item_id": notif_data.get("target_item_id", ""),
21
+ "target_item_title": notif_data.get("target_item_title", ""),
22
+ "content": notif_data.get("content", ""),
23
+ "is_read": False,
24
+ "created_at": int(time.time())
25
+ }
26
+
27
+ def updater(data):
28
+ if target_account not in data:
29
+ data[target_account] = []
30
+ data[target_account].insert(0, notif)
31
+
32
+ db.atomic_update("messages.json", updater, default_data={})
33
+ except Exception as e:
34
+ print(f"⚠️ 通知发送失败 (target={target_account}): {e}")
35
+ # 不抛出异常,让主流程继续
router_items.py CHANGED
@@ -175,66 +175,84 @@ async def update_item(item_id: str, update_data: ItemUpdate, current_user: str =
175
  更新内容接口
176
  🔒 P0安全修复:使用 JWT Token 验证用户身份,而非前端传入的 author 参数
177
  🔄 P7后悔模式:价格修改延迟24小时生效
 
178
  """
179
  if update_data.price is not None:
180
  update_data.price = int(update_data.price)
181
  if update_data.price < 0:
182
  raise HTTPException(status_code=400, detail="🚨 安全拦截:商品价格不能为负数")
183
-
184
- items_db = db.load_data("items.json", default_data=[])
185
- for item in items_db:
186
- if item["id"] == item_id:
187
- # 🔒 P0安全修复:使用 JWT 解析出的真实用户账号进行校验
188
- if item.get("author") != current_user:
189
- raise HTTPException(status_code=403, detail="无权改他人发布内容")
190
-
191
- price_change_info = None
192
-
193
- if update_data.title is not None: item["title"] = update_data.title
194
- if update_data.shortDesc is not None: item["shortDesc"] = update_data.shortDesc
195
- if update_data.fullDesc is not None: item["fullDesc"] = update_data.fullDesc
196
- if update_data.link is not None: item["link"] = update_data.link
197
- if update_data.coverUrl is not None: item["coverUrl"] = update_data.coverUrl
198
- if update_data.imageUrls is not None: item["imageUrls"] = update_data.imageUrls # 🖼️ 效果展示图列表
199
-
200
- # 🔄 P7后悔模式:价格修改延迟24小时生效
201
- if update_data.price is not None:
202
- current_price = item.get("price", 0)
203
- new_price = update_data.price
204
 
205
- if current_price != new_price:
206
- # 设置待生效价格,24小时后生效
207
- import datetime
208
- effective_time = datetime.datetime.now() + datetime.timedelta(hours=24)
209
- item["pending_price"] = new_price
210
- item["pending_price_effective_at"] = effective_time.isoformat()
211
- price_change_info = {
212
- "current_price": current_price,
213
- "new_price": new_price,
214
- "effective_at": effective_time.isoformat()
215
- }
216
- # 不立即修改 price,等待24小时后生效
217
- else:
218
- # 价格未变,清除待生效价格
219
- item["pending_price"] = None
220
- item["pending_price_effective_at"] = None
221
-
222
- if update_data.github_token is not None: item["github_token"] = update_data.github_token
223
- if update_data.netdisk_password is not None: item["netdisk_password"] = update_data.netdisk_password # ☁️
224
- if update_data.is_netdisk is not None: item["is_netdisk"] = update_data.is_netdisk # ☁️
225
- if update_data.is_original is not None: item["is_original"] = update_data.is_original # 🎨
226
-
227
- db.save_data("items.json", items_db)
228
- # 🗂️ 清除排序缓存
229
- sort_cache.invalidate("items:")
230
-
231
- result = {"status": "success"}
232
- if price_change_info:
233
- result["price_change"] = price_change_info
234
- result["message"] = f"价格将于24小时后从{current_price}调整为{new_price}积分"
235
- return result
236
-
237
- raise HTTPException(status_code=404, detail="找不到该内容记录")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
 
239
  # ==========================================
240
  # 🚀 定时任务接口:检查 GitHub 仓库最新版本
@@ -316,34 +334,49 @@ async def get_item_version(item_id: str):
316
  async def delete_item(item_id: str, current_user: str = Depends(require_auth)):
317
  """
318
  删除内容(仅作者或管理员可操作)
 
319
  """
320
- items_db = db.load_data("items.json", default_data=[])
321
- comments_db = db.load_data("comments.json", default_data={})
322
 
323
- for i, item in enumerate(items_db):
324
- if item["id"] == item_id:
325
- # 🔒 权限检查:仅作者或管理员可删除
326
- if not check_ownership(item, current_user, owner_field="author", allow_admin=True):
327
- raise HTTPException(status_code=403, detail="无权删除他人发布的内容")
328
-
329
- # 1. 从 items.json 中删除该条目
330
- items_db.pop(i)
331
- db.save_data("items.json", items_db)
332
-
333
- # 2. 清理关联评论:从 comments.json 中删除该内容的所有评论
334
- if item_id in comments_db:
335
- del comments_db[item_id]
336
- db.save_data("comments.json", comments_db)
337
-
338
- # 3. 清理缓存:使 items.json 和 comments.json 的缓存失效
339
- invalidate_cache("items.json")
340
- invalidate_cache("comments.json")
341
- # 🗂️ 清除排序缓存
342
- sort_cache.invalidate("items:")
343
-
344
- return {"status": "success", "message": "内容已删除"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
345
 
346
- raise HTTPException(status_code=404, detail="找不到该内容记录")
347
 
348
 
349
  @router.post("/api/items/{item_id}/view")
 
175
  更新内容接口
176
  🔒 P0安全修复:使用 JWT Token 验证用户身份,而非前端传入的 author 参数
177
  🔄 P7后悔模式:价格修改延迟24小时生效
178
+ 🔧 Bug修复:使用 atomic_update 避免并发竞态条件
179
  """
180
  if update_data.price is not None:
181
  update_data.price = int(update_data.price)
182
  if update_data.price < 0:
183
  raise HTTPException(status_code=400, detail="🚨 安全拦截:商品价格不能为负数")
184
+
185
+ result_holder = {}
186
+
187
+ def updater(items_db):
188
+ for item in items_db:
189
+ if item["id"] == item_id:
190
+ # 🔒 P0安全复:使用 JWT 解析出真实用户账号进行校验
191
+ if item.get("author") != current_user:
192
+ result_holder["error"] = "forbidden"
193
+ return False
 
 
 
 
 
 
 
 
 
 
 
194
 
195
+ price_change_info = None
196
+
197
+ if update_data.title is not None: item["title"] = update_data.title
198
+ if update_data.shortDesc is not None: item["shortDesc"] = update_data.shortDesc
199
+ if update_data.fullDesc is not None: item["fullDesc"] = update_data.fullDesc
200
+ if update_data.link is not None: item["link"] = update_data.link
201
+ if update_data.coverUrl is not None: item["coverUrl"] = update_data.coverUrl
202
+ if update_data.imageUrls is not None: item["imageUrls"] = update_data.imageUrls # 🖼️ 效果展示图列表
203
+
204
+ # 🔄 P7后悔模式:价格修改延迟24小时生效
205
+ if update_data.price is not None:
206
+ current_price = item.get("price", 0)
207
+ new_price = update_data.price
208
+
209
+ if current_price != new_price:
210
+ # 设置待生效价格,24小时后生效
211
+ import datetime
212
+ effective_time = datetime.datetime.now() + datetime.timedelta(hours=24)
213
+ item["pending_price"] = new_price
214
+ item["pending_price_effective_at"] = effective_time.isoformat()
215
+ price_change_info = {
216
+ "current_price": current_price,
217
+ "new_price": new_price,
218
+ "effective_at": effective_time.isoformat()
219
+ }
220
+ # 不立即修改 price,等待24小时后生效
221
+ else:
222
+ # 价格未变,清除待生效价格
223
+ item["pending_price"] = None
224
+ item["pending_price_effective_at"] = None
225
+
226
+ if update_data.github_token is not None: item["github_token"] = update_data.github_token
227
+ if update_data.netdisk_password is not None: item["netdisk_password"] = update_data.netdisk_password # ☁️
228
+ if update_data.is_netdisk is not None: item["is_netdisk"] = update_data.is_netdisk # ☁️
229
+ if update_data.is_original is not None: item["is_original"] = update_data.is_original # 🎨
230
+
231
+ result_holder["success"] = True
232
+ result_holder["price_change_info"] = price_change_info
233
+ result_holder["current_price"] = price_change_info.get("current_price") if price_change_info else None
234
+ result_holder["new_price"] = price_change_info.get("new_price") if price_change_info else None
235
+ return True
236
+
237
+ result_holder["error"] = "not_found"
238
+ return False
239
+
240
+ db.atomic_update("items.json", updater, default_data=[])
241
+
242
+ if result_holder.get("error") == "forbidden":
243
+ raise HTTPException(status_code=403, detail="无权修改他人发布的内容")
244
+ if result_holder.get("error") == "not_found":
245
+ raise HTTPException(status_code=404, detail="找不到该内容记录")
246
+
247
+ # 🗂️ 清除排序缓存
248
+ sort_cache.invalidate("items:")
249
+
250
+ result = {"status": "success"}
251
+ if result_holder.get("price_change_info"):
252
+ price_change_info = result_holder["price_change_info"]
253
+ result["price_change"] = price_change_info
254
+ result["message"] = f"价格将于24小时后从{price_change_info['current_price']}调整为{price_change_info['new_price']}积分"
255
+ return result
256
 
257
  # ==========================================
258
  # 🚀 定时任务接口:检查 GitHub 仓库最新版本
 
334
  async def delete_item(item_id: str, current_user: str = Depends(require_auth)):
335
  """
336
  删除内容(仅作者或管理员可操作)
337
+ 🔧 Bug修复:使用 atomic_update 避免并发竞态条件
338
  """
339
+ result_holder = {}
 
340
 
341
+ def items_updater(items_db):
342
+ for i, item in enumerate(items_db):
343
+ if item["id"] == item_id:
344
+ # 🔒 权限检查:仅作者或管理员可删除
345
+ if not check_ownership(item, current_user, owner_field="author", allow_admin=True):
346
+ result_holder["error"] = "forbidden"
347
+ return False
348
+
349
+ # 1.items.json 中删除该条目
350
+ items_db.pop(i)
351
+ result_holder["item_deleted"] = True
352
+ return True
353
+
354
+ result_holder["error"] = "not_found"
355
+ return False
356
+
357
+ db.atomic_update("items.json", items_updater, default_data=[])
358
+
359
+ if result_holder.get("error") == "forbidden":
360
+ raise HTTPException(status_code=403, detail="无权删除他人发布的内容")
361
+ if result_holder.get("error") == "not_found":
362
+ raise HTTPException(status_code=404, detail="找不到该内容记录")
363
+
364
+ # 2. 清理关联评论:从 comments.json 中删除该内容的所有评论
365
+ def comments_updater(comments_db):
366
+ if item_id in comments_db:
367
+ del comments_db[item_id]
368
+ return True
369
+ return False
370
+
371
+ db.atomic_update("comments.json", comments_updater, default_data={})
372
+
373
+ # 3. 清理缓存:使 items.json 和 comments.json 的缓存失效
374
+ invalidate_cache("items.json")
375
+ invalidate_cache("comments.json")
376
+ # 🗂️ 清除排序缓存
377
+ sort_cache.invalidate("items:")
378
 
379
+ return {"status": "success", "message": "内容已删除"}
380
 
381
 
382
  @router.post("/api/items/{item_id}/view")