注册码 + 管理员系统:

User 模型新增 is_admin 字段
新增 InviteCode 模型(邀请码表)
注册接口必须提供有效邀请码,使用后自动标记
管理员接口:查看所有用户、启用/禁用用户、生成/删除邀请码
前端新增管理面板页面 /admin,导航栏对管理员显示入口
注册页面新增邀请码输入框
选择性超话签到:

新增 GET /api/v1/accounts/{id}/topics 接口获取超话列表
POST /signin 接口支持 {"topic_indices": [0,1,3]} 选择性签到
新增超话选择页面 /accounts/{id}/topics,支持全选/手动勾选
账号详情页新增"选择超话签到"按钮
This commit is contained in:
2026-03-17 17:05:28 +08:00
parent 2fb27aa714
commit e514a11e62
26 changed files with 649 additions and 18 deletions

View File

@@ -8,7 +8,7 @@ from datetime import datetime
from typing import Dict, List
import httpx
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi import APIRouter, Depends, HTTPException, status, Body
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
@@ -249,6 +249,25 @@ async def verify_account(
)
@router.get("/{account_id}/topics")
async def list_topics(
account_id: str,
user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""获取账号关注的超话列表,供用户勾选签到。"""
account = await _get_owned_account(account_id, user, db)
key = _encryption_key()
try:
cookie_str = decrypt_cookie(account.encrypted_cookies, account.iv, key)
except Exception:
return error_response("Cookie 解密失败", "COOKIE_ERROR", status_code=400)
topics = await _get_super_topics(cookie_str, account.weibo_user_id)
return success_response({"topics": topics, "total": len(topics)})
# ---- MANUAL SIGNIN ----
async def _get_super_topics(cookie_str: str, weibo_uid: str = "") -> List[dict]:
@@ -384,10 +403,12 @@ async def manual_signin(
account_id: str,
user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
body: dict = Body(default=None),
):
"""
Manually trigger sign-in for all followed super topics.
Verifies cookie first, fetches topic list, signs each one, writes logs.
Manually trigger sign-in for selected (or all) super topics.
Body (optional): {"topic_indices": [0, 1, 3]} — indices of topics to sign.
If omitted or empty, signs all topics.
"""
account = await _get_owned_account(account_id, user, db)
key = _encryption_key()
@@ -421,6 +442,18 @@ async def manual_signin(
"No super topics found for this account",
)
# Filter topics if specific indices provided
selected_indices = None
if body and isinstance(body, dict):
selected_indices = body.get("topic_indices")
if selected_indices and isinstance(selected_indices, list):
topics = [topics[i] for i in selected_indices if 0 <= i < len(topics)]
if not topics:
return success_response(
{"signed": 0, "already_signed": 0, "failed": 0, "topics": []},
"No valid topics selected",
)
# Sign each topic
results = []
signed = already = failed = 0