2026-03-18 17:18:23 +08:00
|
|
|
from sqlalchemy import Column, DateTime, Integer, String
|
|
|
|
|
from sqlalchemy.orm import relationship
|
|
|
|
|
|
|
|
|
|
from app.core.db import Base, utc_now
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class User(Base):
|
|
|
|
|
__tablename__ = "users"
|
|
|
|
|
|
|
|
|
|
id = Column(String, primary_key=True)
|
|
|
|
|
phone = Column(String, unique=True, nullable=False, index=True)
|
|
|
|
|
password_hash = Column(String, nullable=False)
|
|
|
|
|
email = Column(String, unique=True, nullable=True)
|
|
|
|
|
openid = Column(String, unique=True, nullable=True)
|
|
|
|
|
nickname = Column(String, nullable=False)
|
|
|
|
|
avatar_url = Column(String, nullable=True)
|
|
|
|
|
subscription_type = Column(String, default="free")
|
|
|
|
|
subscription_expires_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
|
created_at = Column(DateTime(timezone=True), default=utc_now)
|
|
|
|
|
birth_year = Column(Integer, nullable=True)
|
|
|
|
|
birth_place = Column(String, nullable=True)
|
|
|
|
|
grew_up_place = Column(String, nullable=True)
|
|
|
|
|
occupation = Column(String, nullable=True)
|
feat(i18n): persist language preference and thread through chat, memoir, TTS
- Add users.language_preference (Alembic 0018, default zh); capture at signup/SMS
only; expose on auth and profile APIs
- Lite English prompts for chat and memoir; localized stage labels and agent
names (Life Echo / 岁月知己)
- Tencent TTS: language-aware synthesis, ModelType=1 for 501004, English chunking
- WebSocket pipeline: emit all AGENT_RESPONSE segments when TTS cancels; INFO logs
for tts_this_turn and TTS decisions; on-demand TTS logging
- Expo: device language on auth, i18n tiers/agent name, [SPLIT] streaming UX fixes
- Tests for migration, prompts, pipeline, router tts_this_turn, reply segments
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 16:16:49 +08:00
|
|
|
language_preference = Column(
|
|
|
|
|
String(8), nullable=False, default="zh", server_default="zh"
|
|
|
|
|
)
|
2026-03-18 17:18:23 +08:00
|
|
|
|
|
|
|
|
conversations = relationship("Conversation", back_populates="user")
|
|
|
|
|
chapters = relationship("Chapter", back_populates="user")
|
2026-03-20 10:30:07 +08:00
|
|
|
stories = relationship("Story", back_populates="user")
|
2026-03-18 17:18:23 +08:00
|
|
|
books = relationship("Book", back_populates="user")
|
|
|
|
|
orders = relationship("Order", back_populates="user", cascade="all, delete-orphan")
|
|
|
|
|
memoir_state = relationship(
|
2026-03-19 14:36:14 +08:00
|
|
|
"MemoirState",
|
|
|
|
|
back_populates="user",
|
|
|
|
|
uselist=False,
|
|
|
|
|
cascade="all, delete-orphan",
|
2026-03-18 17:18:23 +08:00
|
|
|
)
|
|
|
|
|
refresh_tokens = relationship(
|
|
|
|
|
"RefreshToken", back_populates="user", cascade="all, delete-orphan"
|
|
|
|
|
)
|