Spaces:
Running
Running
Upload router_items.py
Browse files- router_items.py +54 -30
router_items.py
CHANGED
|
@@ -2,12 +2,25 @@
|
|
| 2 |
from fastapi import APIRouter, HTTPException
|
| 3 |
import time
|
| 4 |
import uuid
|
|
|
|
| 5 |
import 数据库连接 as db
|
| 6 |
-
# 【核心修改】:在顶部引入我们刚才新增的 ItemUpdate 模型
|
| 7 |
from models import ItemCreate, ItemUpdate
|
| 8 |
|
| 9 |
router = APIRouter()
|
| 10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
@router.get("/api/items")
|
| 12 |
async def get_items(type: str = "tool", sort: str = "time", limit: int = 20):
|
| 13 |
items_db = db.load_data("items.json", default_data=[])
|
|
@@ -27,18 +40,43 @@ async def get_creators(sort: str = "downloads", limit: int = 20):
|
|
| 27 |
users_db = db.load_data("users.json", default_data={})
|
| 28 |
items_db = db.load_data("items.json", default_data=[])
|
| 29 |
comments_db = db.load_data("comments.json", default_data={})
|
|
|
|
| 30 |
creators = []
|
|
|
|
|
|
|
| 31 |
for account, u in users_db.items():
|
| 32 |
u_items = [i for i in items_db if i.get("author") == account]
|
| 33 |
-
|
| 34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
creators.append({
|
| 36 |
"account": account, "name": u.get("name", account), "avatar": u.get("avatarDataUrl", "https://via.placeholder.com/150"),
|
| 37 |
"shortDesc": u.get("intro", "这个人很懒,什么都没写..."), "fullDesc": u.get("intro", "这个人很懒,什么都没写..."),
|
| 38 |
-
"likes": sum(i.get("likes", 0) for i in u_items), "favorites": sum(i.get("favorites", 0) for i in u_items),
|
|
|
|
| 39 |
"toolsCount": tools_count, "appsCount": apps_count, "followers": len(u.get("followers", [])), "created_at": u.get("created_at", 0),
|
| 40 |
-
"commentsData": comments_db.get(account, []),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
})
|
|
|
|
| 42 |
if sort == "likes": creators.sort(key=lambda x: x.get("likes", 0), reverse=True)
|
| 43 |
elif sort == "favorites": creators.sort(key=lambda x: x.get("favorites", 0), reverse=True)
|
| 44 |
elif sort == "downloads": creators.sort(key=lambda x: x.get("downloads", 0), reverse=True)
|
|
@@ -51,72 +89,58 @@ async def create_item(item: ItemCreate):
|
|
| 51 |
new_item = {
|
| 52 |
"id": f"{item.type}_{int(time.time())}_{uuid.uuid4().hex[:6]}", "type": item.type, "title": item.title, "author": item.author,
|
| 53 |
"shortDesc": item.shortDesc, "fullDesc": item.fullDesc, "link": item.link, "coverUrl": item.coverUrl, "price": item.price,
|
| 54 |
-
"likes": 0, "favorites": 0, "comments": 0, "uses": 0, "created_at": int(time.time()), "liked_by": [], "favorited_by": []
|
| 55 |
}
|
| 56 |
items_db.insert(0, new_item)
|
| 57 |
db.save_data("items.json", items_db)
|
| 58 |
return {"status": "success", "data": new_item}
|
| 59 |
|
| 60 |
-
# =========================================================
|
| 61 |
-
# 【核心新增】:以下为修改与删除相关接口
|
| 62 |
-
# =========================================================
|
| 63 |
-
|
| 64 |
@router.put("/api/items/{item_id}")
|
| 65 |
async def update_item(item_id: str, update_data: ItemUpdate, author: str):
|
| 66 |
items_db = db.load_data("items.json", default_data=[])
|
| 67 |
for item in items_db:
|
| 68 |
if item["id"] == item_id:
|
| 69 |
-
|
| 70 |
-
if item.get("author") != author:
|
| 71 |
-
raise HTTPException(status_code=403, detail="无权修改他人发布的内容")
|
| 72 |
-
|
| 73 |
-
# 只更新存在的字段,不影响浏览量点赞等数据
|
| 74 |
if update_data.title is not None: item["title"] = update_data.title
|
| 75 |
if update_data.shortDesc is not None: item["shortDesc"] = update_data.shortDesc
|
| 76 |
if update_data.fullDesc is not None: item["fullDesc"] = update_data.fullDesc
|
| 77 |
if update_data.link is not None: item["link"] = update_data.link
|
| 78 |
if update_data.coverUrl is not None: item["coverUrl"] = update_data.coverUrl
|
| 79 |
if update_data.price is not None: item["price"] = update_data.price
|
| 80 |
-
|
| 81 |
db.save_data("items.json", items_db)
|
| 82 |
return {"status": "success"}
|
| 83 |
-
|
| 84 |
raise HTTPException(status_code=404, detail="找不到该内容记录")
|
| 85 |
|
| 86 |
@router.delete("/api/items/{item_id}")
|
| 87 |
async def delete_item(item_id: str, author: str):
|
| 88 |
items_db = db.load_data("items.json", default_data=[])
|
| 89 |
target_idx = next((i for i, item in enumerate(items_db) if item["id"] == item_id), None)
|
|
|
|
|
|
|
| 90 |
|
| 91 |
-
if target_idx is None:
|
| 92 |
-
raise HTTPException(status_code=404, detail="找不到该内容记录")
|
| 93 |
-
if items_db[target_idx].get("author") != author:
|
| 94 |
-
raise HTTPException(status_code=403, detail="无权删除他人发布的内容")
|
| 95 |
-
|
| 96 |
-
# 1. 删除发布的内容
|
| 97 |
items_db.pop(target_idx)
|
| 98 |
db.save_data("items.json", items_db)
|
| 99 |
|
| 100 |
-
# 2. 同步清理该内容下的所有关联评论记录
|
| 101 |
comments_db = db.load_data("comments.json", default_data={})
|
| 102 |
if item_id in comments_db:
|
| 103 |
del comments_db[item_id]
|
| 104 |
db.save_data("comments.json", comments_db)
|
| 105 |
-
|
| 106 |
return {"status": "success"}
|
| 107 |
|
| 108 |
-
# =========================================================
|
| 109 |
-
# 【核心新增】:使用量累加同步接口
|
| 110 |
-
# =========================================================
|
| 111 |
@router.post("/api/items/{item_id}/use")
|
| 112 |
async def record_item_use(item_id: str):
|
| 113 |
-
"""记录资源被获取/使用的次数"""
|
| 114 |
items_db = db.load_data("items.json", default_data=[])
|
|
|
|
| 115 |
|
| 116 |
for item in items_db:
|
| 117 |
if item["id"] == item_id:
|
| 118 |
-
# 原有的使用量 + 1 (如果没有则默认为0再+1)
|
| 119 |
item["uses"] = item.get("uses", 0) + 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
db.save_data("items.json", items_db)
|
| 121 |
return {"status": "success", "uses": item["uses"]}
|
| 122 |
|
|
|
|
| 2 |
from fastapi import APIRouter, HTTPException
|
| 3 |
import time
|
| 4 |
import uuid
|
| 5 |
+
import datetime # 【新增】:处理时间序列
|
| 6 |
import 数据库连接 as db
|
|
|
|
| 7 |
from models import ItemCreate, ItemUpdate
|
| 8 |
|
| 9 |
router = APIRouter()
|
| 10 |
|
| 11 |
+
# 【新增】:获取最近 6 个月的年月字符串列表 (例如: ['2025-10', '2025-11', ..., '2026-03'])
|
| 12 |
+
def get_last_6_months():
|
| 13 |
+
res = []
|
| 14 |
+
today = datetime.date.today()
|
| 15 |
+
for i in range(5, -1, -1):
|
| 16 |
+
m = today.month - i
|
| 17 |
+
y = today.year
|
| 18 |
+
while m <= 0:
|
| 19 |
+
m += 12
|
| 20 |
+
y -= 1
|
| 21 |
+
res.append(f"{y}-{m:02d}")
|
| 22 |
+
return res
|
| 23 |
+
|
| 24 |
@router.get("/api/items")
|
| 25 |
async def get_items(type: str = "tool", sort: str = "time", limit: int = 20):
|
| 26 |
items_db = db.load_data("items.json", default_data=[])
|
|
|
|
| 40 |
users_db = db.load_data("users.json", default_data={})
|
| 41 |
items_db = db.load_data("items.json", default_data=[])
|
| 42 |
comments_db = db.load_data("comments.json", default_data={})
|
| 43 |
+
|
| 44 |
creators = []
|
| 45 |
+
months = get_last_6_months()
|
| 46 |
+
|
| 47 |
for account, u in users_db.items():
|
| 48 |
u_items = [i for i in items_db if i.get("author") == account]
|
| 49 |
+
|
| 50 |
+
# 【核心修正】:计算真实的聚合增长曲线
|
| 51 |
+
trend_tools = {m: 0 for m in months}
|
| 52 |
+
trend_apps = {m: 0 for m in months}
|
| 53 |
+
tools_count = 0
|
| 54 |
+
apps_count = 0
|
| 55 |
+
|
| 56 |
+
for i in u_items:
|
| 57 |
+
itype = i.get("type")
|
| 58 |
+
history = i.get("use_history", {})
|
| 59 |
+
if itype == "tool":
|
| 60 |
+
tools_count += 1
|
| 61 |
+
for m in months: trend_tools[m] += history.get(m, 0)
|
| 62 |
+
elif itype == "app":
|
| 63 |
+
apps_count += 1
|
| 64 |
+
for m in months: trend_apps[m] += history.get(m, 0)
|
| 65 |
+
|
| 66 |
creators.append({
|
| 67 |
"account": account, "name": u.get("name", account), "avatar": u.get("avatarDataUrl", "https://via.placeholder.com/150"),
|
| 68 |
"shortDesc": u.get("intro", "这个人很懒,什么都没写..."), "fullDesc": u.get("intro", "这个人很懒,什么都没写..."),
|
| 69 |
+
"likes": sum(i.get("likes", 0) for i in u_items), "favorites": sum(i.get("favorites", 0) for i in u_items),
|
| 70 |
+
"downloads": sum(i.get("uses", 0) for i in u_items), # 创作者总下载量
|
| 71 |
"toolsCount": tools_count, "appsCount": apps_count, "followers": len(u.get("followers", [])), "created_at": u.get("created_at", 0),
|
| 72 |
+
"commentsData": comments_db.get(account, []),
|
| 73 |
+
"trendData": {
|
| 74 |
+
"months": months,
|
| 75 |
+
"tools": [trend_tools[m] for m in months],
|
| 76 |
+
"apps": [trend_apps[m] for m in months]
|
| 77 |
+
}
|
| 78 |
})
|
| 79 |
+
|
| 80 |
if sort == "likes": creators.sort(key=lambda x: x.get("likes", 0), reverse=True)
|
| 81 |
elif sort == "favorites": creators.sort(key=lambda x: x.get("favorites", 0), reverse=True)
|
| 82 |
elif sort == "downloads": creators.sort(key=lambda x: x.get("downloads", 0), reverse=True)
|
|
|
|
| 89 |
new_item = {
|
| 90 |
"id": f"{item.type}_{int(time.time())}_{uuid.uuid4().hex[:6]}", "type": item.type, "title": item.title, "author": item.author,
|
| 91 |
"shortDesc": item.shortDesc, "fullDesc": item.fullDesc, "link": item.link, "coverUrl": item.coverUrl, "price": item.price,
|
| 92 |
+
"likes": 0, "favorites": 0, "comments": 0, "uses": 0, "use_history": {}, "created_at": int(time.time()), "liked_by": [], "favorited_by": []
|
| 93 |
}
|
| 94 |
items_db.insert(0, new_item)
|
| 95 |
db.save_data("items.json", items_db)
|
| 96 |
return {"status": "success", "data": new_item}
|
| 97 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
@router.put("/api/items/{item_id}")
|
| 99 |
async def update_item(item_id: str, update_data: ItemUpdate, author: str):
|
| 100 |
items_db = db.load_data("items.json", default_data=[])
|
| 101 |
for item in items_db:
|
| 102 |
if item["id"] == item_id:
|
| 103 |
+
if item.get("author") != author: raise HTTPException(status_code=403, detail="无权修改他人发布的内容")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
if update_data.title is not None: item["title"] = update_data.title
|
| 105 |
if update_data.shortDesc is not None: item["shortDesc"] = update_data.shortDesc
|
| 106 |
if update_data.fullDesc is not None: item["fullDesc"] = update_data.fullDesc
|
| 107 |
if update_data.link is not None: item["link"] = update_data.link
|
| 108 |
if update_data.coverUrl is not None: item["coverUrl"] = update_data.coverUrl
|
| 109 |
if update_data.price is not None: item["price"] = update_data.price
|
|
|
|
| 110 |
db.save_data("items.json", items_db)
|
| 111 |
return {"status": "success"}
|
|
|
|
| 112 |
raise HTTPException(status_code=404, detail="找不到该内容记录")
|
| 113 |
|
| 114 |
@router.delete("/api/items/{item_id}")
|
| 115 |
async def delete_item(item_id: str, author: str):
|
| 116 |
items_db = db.load_data("items.json", default_data=[])
|
| 117 |
target_idx = next((i for i, item in enumerate(items_db) if item["id"] == item_id), None)
|
| 118 |
+
if target_idx is None: raise HTTPException(status_code=404, detail="找不到该内容记录")
|
| 119 |
+
if items_db[target_idx].get("author") != author: raise HTTPException(status_code=403, detail="无权删除他人发布的内容")
|
| 120 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
items_db.pop(target_idx)
|
| 122 |
db.save_data("items.json", items_db)
|
| 123 |
|
|
|
|
| 124 |
comments_db = db.load_data("comments.json", default_data={})
|
| 125 |
if item_id in comments_db:
|
| 126 |
del comments_db[item_id]
|
| 127 |
db.save_data("comments.json", comments_db)
|
|
|
|
| 128 |
return {"status": "success"}
|
| 129 |
|
|
|
|
|
|
|
|
|
|
| 130 |
@router.post("/api/items/{item_id}/use")
|
| 131 |
async def record_item_use(item_id: str):
|
| 132 |
+
"""记录资源被获取/使用的次数,并计入时间序列"""
|
| 133 |
items_db = db.load_data("items.json", default_data=[])
|
| 134 |
+
current_month = datetime.date.today().strftime("%Y-%m")
|
| 135 |
|
| 136 |
for item in items_db:
|
| 137 |
if item["id"] == item_id:
|
|
|
|
| 138 |
item["uses"] = item.get("uses", 0) + 1
|
| 139 |
+
# 初始化时间序列历史并累加
|
| 140 |
+
if "use_history" not in item:
|
| 141 |
+
item["use_history"] = {}
|
| 142 |
+
item["use_history"][current_month] = item["use_history"].get(current_month, 0) + 1
|
| 143 |
+
|
| 144 |
db.save_data("items.json", items_db)
|
| 145 |
return {"status": "success", "uses": item["uses"]}
|
| 146 |
|