Spaces:
Running
Running
Upload 2 files
Browse files- router_messages.py +42 -18
- router_wallet.py +2 -1
router_messages.py
CHANGED
|
@@ -9,25 +9,31 @@ from models import PrivateMessage
|
|
| 9 |
|
| 10 |
router = APIRouter()
|
| 11 |
|
| 12 |
-
# === 用于接收系统公告的请求体模型 ===
|
| 13 |
class SystemAnnouncement(BaseModel):
|
| 14 |
admin_account: str
|
| 15 |
content: str
|
| 16 |
|
| 17 |
-
# === 发布系统公告接口 (仅限管理员) ===
|
| 18 |
@router.post("/api/system/announcement")
|
| 19 |
async def publish_announcement(ann: SystemAnnouncement):
|
|
|
|
| 20 |
if ann.admin_account != "666666":
|
| 21 |
raise HTTPException(status_code=403, detail="无权发布系统公告")
|
| 22 |
|
| 23 |
announcements_db = db.load_data("announcements.json", default_data=[])
|
| 24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
new_ann = {
|
| 26 |
"id": f"sys_{int(time.time())}_{uuid.uuid4().hex[:6]}",
|
| 27 |
"type": "system",
|
| 28 |
"from_user": "666666",
|
| 29 |
"from_name": "官方团队",
|
| 30 |
-
"from_avatar": "https://via.placeholder.com/150/FF9800/FFFFFF?text=Sys",
|
| 31 |
"content": ann.content,
|
| 32 |
"created_at": int(time.time())
|
| 33 |
}
|
|
@@ -37,6 +43,7 @@ async def publish_announcement(ann: SystemAnnouncement):
|
|
| 37 |
|
| 38 |
return {"status": "success", "data": new_ann}
|
| 39 |
|
|
|
|
| 40 |
@router.post("/api/messages/private")
|
| 41 |
async def send_private_message(msg: PrivateMessage):
|
| 42 |
chats_db = db.load_data("chats.json", default_data={})
|
|
@@ -90,11 +97,13 @@ async def get_chat_history(account: str, target_account: str):
|
|
| 90 |
modified = False
|
| 91 |
|
| 92 |
for m in msgs:
|
|
|
|
| 93 |
if now - m.get("created_at", 0) < seven_days:
|
| 94 |
valid_msgs.append(m)
|
| 95 |
else:
|
| 96 |
modified = True
|
| 97 |
|
|
|
|
| 98 |
if m["receiver"] == account and not m.get("is_read"):
|
| 99 |
m["is_read"] = True
|
| 100 |
modified = True
|
|
@@ -110,29 +119,43 @@ async def get_messages(account: str):
|
|
| 110 |
msgs_db = db.load_data("messages.json", default_data={})
|
| 111 |
user_msgs = msgs_db.get(account, [])
|
| 112 |
|
| 113 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
announcements_db = db.load_data("announcements.json", default_data=[])
|
| 115 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
|
| 117 |
injected = False
|
| 118 |
for ann in announcements_db:
|
| 119 |
-
if ann.get("id") not in user_msg_ids:
|
| 120 |
-
|
|
|
|
| 121 |
new_sys_msg["is_read"] = False
|
| 122 |
new_sys_msg["receiver"] = account
|
| 123 |
user_msgs.append(new_sys_msg)
|
| 124 |
injected = True
|
| 125 |
|
| 126 |
if injected:
|
| 127 |
-
|
| 128 |
-
|
|
|
|
| 129 |
|
| 130 |
now = int(time.time())
|
| 131 |
seven_days = 7 * 24 * 3600
|
| 132 |
valid = []
|
|
|
|
| 133 |
modified = injected
|
| 134 |
|
| 135 |
for m in user_msgs:
|
|
|
|
| 136 |
if not m.get("is_read") or (now - m.get("created_at", 0) < seven_days):
|
| 137 |
valid.append(m)
|
| 138 |
else:
|
|
@@ -142,7 +165,7 @@ async def get_messages(account: str):
|
|
| 142 |
msgs_db[account] = valid
|
| 143 |
db.save_data("messages.json", msgs_db)
|
| 144 |
|
| 145 |
-
return {"status": "success", "data": valid, "unread_count": sum(1 for m in valid if
|
| 146 |
|
| 147 |
@router.post("/api/messages/{account}/read")
|
| 148 |
async def mark_messages_read(account: str):
|
|
@@ -150,13 +173,14 @@ async def mark_messages_read(account: str):
|
|
| 150 |
user_msgs = msgs_db.get(account, [])
|
| 151 |
modified = False
|
| 152 |
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
m
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
|
|
|
| 161 |
|
| 162 |
return {"status": "success"}
|
|
|
|
| 9 |
|
| 10 |
router = APIRouter()
|
| 11 |
|
| 12 |
+
# === 新增:用于接收系统公告的请求体模型 ===
|
| 13 |
class SystemAnnouncement(BaseModel):
|
| 14 |
admin_account: str
|
| 15 |
content: str
|
| 16 |
|
| 17 |
+
# === 新增:发布系统公告接口 (仅限管理员) ===
|
| 18 |
@router.post("/api/system/announcement")
|
| 19 |
async def publish_announcement(ann: SystemAnnouncement):
|
| 20 |
+
# 鉴权:只有指定的官方账号才能发公告
|
| 21 |
if ann.admin_account != "666666":
|
| 22 |
raise HTTPException(status_code=403, detail="无权发布系统公告")
|
| 23 |
|
| 24 |
announcements_db = db.load_data("announcements.json", default_data=[])
|
| 25 |
|
| 26 |
+
# 【防御性修复】:确保就算之前存成了别的格式,这里也强制转换为列表,防止 append 报错
|
| 27 |
+
if not isinstance(announcements_db, list):
|
| 28 |
+
announcements_db = []
|
| 29 |
+
|
| 30 |
+
# 构造一条标准的系统公告数据结构
|
| 31 |
new_ann = {
|
| 32 |
"id": f"sys_{int(time.time())}_{uuid.uuid4().hex[:6]}",
|
| 33 |
"type": "system",
|
| 34 |
"from_user": "666666",
|
| 35 |
"from_name": "官方团队",
|
| 36 |
+
"from_avatar": "https://via.placeholder.com/150/FF9800/FFFFFF?text=Sys", # 专属的橙色系统头像
|
| 37 |
"content": ann.content,
|
| 38 |
"created_at": int(time.time())
|
| 39 |
}
|
|
|
|
| 43 |
|
| 44 |
return {"status": "success", "data": new_ann}
|
| 45 |
|
| 46 |
+
|
| 47 |
@router.post("/api/messages/private")
|
| 48 |
async def send_private_message(msg: PrivateMessage):
|
| 49 |
chats_db = db.load_data("chats.json", default_data={})
|
|
|
|
| 97 |
modified = False
|
| 98 |
|
| 99 |
for m in msgs:
|
| 100 |
+
# 聊天记录也遵循 7 天冷数据截断
|
| 101 |
if now - m.get("created_at", 0) < seven_days:
|
| 102 |
valid_msgs.append(m)
|
| 103 |
else:
|
| 104 |
modified = True
|
| 105 |
|
| 106 |
+
# 本次访问即为已读
|
| 107 |
if m["receiver"] == account and not m.get("is_read"):
|
| 108 |
m["is_read"] = True
|
| 109 |
modified = True
|
|
|
|
| 119 |
msgs_db = db.load_data("messages.json", default_data={})
|
| 120 |
user_msgs = msgs_db.get(account, [])
|
| 121 |
|
| 122 |
+
# 【防御性修复】:确保 user_msgs 是列表
|
| 123 |
+
if not isinstance(user_msgs, list):
|
| 124 |
+
user_msgs = []
|
| 125 |
+
|
| 126 |
+
# =========================================================
|
| 127 |
+
# 系统公告懒加载注入逻辑 (Lazy Push)
|
| 128 |
+
# =========================================================
|
| 129 |
announcements_db = db.load_data("announcements.json", default_data=[])
|
| 130 |
+
if not isinstance(announcements_db, list):
|
| 131 |
+
announcements_db = []
|
| 132 |
+
|
| 133 |
+
# 获取用户已经拥有的消息 ID 集合,用于快速比对(加入防脏数据判断)
|
| 134 |
+
user_msg_ids = {m.get("id") for m in user_msgs if isinstance(m, dict)}
|
| 135 |
|
| 136 |
injected = False
|
| 137 |
for ann in announcements_db:
|
| 138 |
+
if isinstance(ann, dict) and ann.get("id") not in user_msg_ids:
|
| 139 |
+
# 如果是新公告,构造一条个人消息注入进去
|
| 140 |
+
new_sys_msg = dict(ann) # 浅拷贝,防止修改全局数据
|
| 141 |
new_sys_msg["is_read"] = False
|
| 142 |
new_sys_msg["receiver"] = account
|
| 143 |
user_msgs.append(new_sys_msg)
|
| 144 |
injected = True
|
| 145 |
|
| 146 |
if injected:
|
| 147 |
+
# 如果注入了新公告,按照时间重新倒序排列,确保最新的消息/公告在最上面
|
| 148 |
+
user_msgs.sort(key=lambda x: x.get("created_at", 0) if isinstance(x, dict) else 0, reverse=True)
|
| 149 |
+
# =========================================================
|
| 150 |
|
| 151 |
now = int(time.time())
|
| 152 |
seven_days = 7 * 24 * 3600
|
| 153 |
valid = []
|
| 154 |
+
# 如果刚才注入了新公告,说明用户的消息列表被修改了,必须触发保存
|
| 155 |
modified = injected
|
| 156 |
|
| 157 |
for m in user_msgs:
|
| 158 |
+
if not isinstance(m, dict): continue
|
| 159 |
if not m.get("is_read") or (now - m.get("created_at", 0) < seven_days):
|
| 160 |
valid.append(m)
|
| 161 |
else:
|
|
|
|
| 165 |
msgs_db[account] = valid
|
| 166 |
db.save_data("messages.json", msgs_db)
|
| 167 |
|
| 168 |
+
return {"status": "success", "data": valid, "unread_count": sum(1 for m in valid if m.get("is_read") is False)}
|
| 169 |
|
| 170 |
@router.post("/api/messages/{account}/read")
|
| 171 |
async def mark_messages_read(account: str):
|
|
|
|
| 173 |
user_msgs = msgs_db.get(account, [])
|
| 174 |
modified = False
|
| 175 |
|
| 176 |
+
if isinstance(user_msgs, list):
|
| 177 |
+
for m in user_msgs:
|
| 178 |
+
if isinstance(m, dict) and not m.get("is_read"):
|
| 179 |
+
m["is_read"] = True
|
| 180 |
+
modified = True
|
| 181 |
+
|
| 182 |
+
if modified:
|
| 183 |
+
msgs_db[account] = user_msgs
|
| 184 |
+
db.save_data("messages.json", msgs_db)
|
| 185 |
|
| 186 |
return {"status": "success"}
|
router_wallet.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
# router_wallet.py
|
| 2 |
from fastapi import APIRouter, Depends, HTTPException, Request
|
|
|
|
| 3 |
from sqlalchemy.orm import Session
|
| 4 |
import time
|
| 5 |
import uuid
|
|
@@ -104,7 +105,7 @@ async def tip_user(req: TipRequest, db: Session = Depends(get_db)):
|
|
| 104 |
|
| 105 |
@router.post("/api/wallet/withdraw")
|
| 106 |
async def submit_withdraw(req: WithdrawRequest, db: Session = Depends(get_db)):
|
| 107 |
-
# 1. 验证码校验
|
| 108 |
verify_data = VERIFY_CODES.get(req.account)
|
| 109 |
if not verify_data or verify_data["code"] != req.code or verify_data["action"] != "withdraw":
|
| 110 |
raise HTTPException(status_code=400, detail="验证码无效或已过期")
|
|
|
|
| 1 |
# router_wallet.py
|
| 2 |
from fastapi import APIRouter, Depends, HTTPException, Request
|
| 3 |
+
from fastapi.responses import Response # 【必须导入的模块,否则支付宝回调报 500】
|
| 4 |
from sqlalchemy.orm import Session
|
| 5 |
import time
|
| 6 |
import uuid
|
|
|
|
| 105 |
|
| 106 |
@router.post("/api/wallet/withdraw")
|
| 107 |
async def submit_withdraw(req: WithdrawRequest, db: Session = Depends(get_db)):
|
| 108 |
+
# 1. 验证码校验
|
| 109 |
verify_data = VERIFY_CODES.get(req.account)
|
| 110 |
if not verify_data or verify_data["code"] != req.code or verify_data["action"] != "withdraw":
|
| 111 |
raise HTTPException(status_code=400, detail="验证码无效或已过期")
|