feat: silent wechat login, marketing tab optimization, admin page foundation

- Add silent WeChat login for MP/browser environments
- Fix Python 3.6 compatibility (remove typing.Annotated usage)
- Marketing page: tab-based content generation with category support
- Translate page: add auto-detect language default
- Homepage: add TTS playback, announcement ticker, remove redundant quick-actions
- Fix FAB button overlap with custom tabbar on customers/quotation pages
- Make openai/anthropic imports lazy for Python 3.6 compat
This commit is contained in:
TradeMate Dev
2026-05-14 00:30:48 +08:00
parent f70dd24c7d
commit 23a31f7c00
30 changed files with 485 additions and 269 deletions
+16 -7
View File
@@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends, HTTPException, status, Header
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from typing import Annotated, Optional
from typing import Optional
import uuid
from app.database import get_db
from app.models.user import User
@@ -31,7 +31,7 @@ class RefreshRequest(BaseModel):
@router.post("/register")
async def register(data: RegisterRequest, db: Annotated[AsyncSession, Depends(get_db)] = None):
async def register(data: RegisterRequest, db: AsyncSession = Depends(get_db)):
existing = await db.execute(select(User).where(User.phone == data.phone))
if existing.scalar_one_or_none():
raise HTTPException(status_code=400, detail="Phone already registered")
@@ -56,8 +56,8 @@ async def register(data: RegisterRequest, db: Annotated[AsyncSession, Depends(ge
@router.post("/login", response_model=LoginResponse)
async def login(
form: Annotated[OAuth2PasswordRequestForm, Depends()],
db: Annotated[AsyncSession, Depends(get_db)] = None,
form: OAuth2PasswordRequestForm = Depends(),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(User).where(User.phone == form.username))
user = result.scalar_one_or_none()
@@ -128,7 +128,7 @@ async def refresh(data: RefreshRequest):
@router.get("/me")
async def get_me(
authorization: Optional[str] = Header(None, alias="Authorization"),
db: Annotated[AsyncSession, Depends(get_db)] = None,
db: AsyncSession = Depends(get_db),
):
if not authorization or not authorization.startswith("Bearer "):
raise HTTPException(status_code=401, detail="Missing token")
@@ -178,8 +178,17 @@ class WeChatLoginRequest(BaseModel):
iv: str = ""
@router.get("/wechat/config")
async def wechat_config():
from app.config import settings
return {
"available": bool(settings.WECHAT_APP_ID and settings.WECHAT_APP_SECRET),
"app_id": settings.WECHAT_APP_ID or "",
}
@router.post("/wechat-login")
async def wechat_login(data: WeChatLoginRequest, db: Annotated[AsyncSession, Depends(get_db)] = None):
async def wechat_login(data: WeChatLoginRequest, db: AsyncSession = Depends(get_db)):
from app.services.wechat import wechat_service
session = await wechat_service.code2session(data.code)
@@ -216,7 +225,7 @@ async def wechat_login(data: WeChatLoginRequest, db: Annotated[AsyncSession, Dep
async def update_settings(
data: SettingsUpdate,
authorization: Optional[str] = Header(None, alias="Authorization"),
db: Annotated[AsyncSession, Depends(get_db)] = None,
db: AsyncSession = Depends(get_db),
):
if not authorization or not authorization.startswith("Bearer "):
raise HTTPException(status_code=401, detail="Missing token")