import uuid from fastapi import APIRouter, Depends, status from app.core.config import settings from app.core.cos_url_keys import avatar_url_for_api_response from app.core.dependencies import get_object_storage from app.core.deps_types import CurrentUserDep from app.core.errors import BadRequestError, NotFoundError from app.core.logging import get_logger from app.core.openapi import error_responses from app.features.user.deps import get_user_service from app.features.user.schemas import ( FeedbackResponse, PurgeUserDataRequest, PurgeUserDataResponse, SubmitFeedbackRequest, TestSubscriptionRequest, TestSubscriptionResponse, UpdateUserProfileRequest, UserProfileResponse, ) from app.features.user.service import UserService from app.features.user.service import _coerce_language as _coerce_language_token from app.ports.storage import ObjectStorage logger = get_logger(__name__) router = APIRouter( prefix="/api/user", tags=["user"], responses=error_responses(401, 403, 404), ) feedback_router = APIRouter( prefix="/api/feedback", tags=["feedback"], responses=error_responses(401, 403, 404), ) @router.get("/profile", response_model=UserProfileResponse) async def get_user_profile( current_user: CurrentUserDep, ): return UserProfileResponse( id=current_user.id, phone=current_user.phone, email=current_user.email, nickname=current_user.nickname, avatar_url=avatar_url_for_api_response(current_user.avatar_url), subscription_type=current_user.subscription_type, created_at=current_user.created_at.isoformat(), birth_year=current_user.birth_year, birth_place=current_user.birth_place, grew_up_place=current_user.grew_up_place, occupation=current_user.occupation, language_preference=_coerce_language_token( getattr(current_user, "language_preference", "zh") ), ) @router.put("/profile", response_model=UserProfileResponse) async def update_user_profile( body: UpdateUserProfileRequest, current_user: CurrentUserDep, service: UserService = Depends(get_user_service), ): logger.info( "更新用户档案 user_id={} fields={}", current_user.id, sorted(body.model_fields_set), ) return await service.update_profile(current_user.id, body) @router.post("/data/purge", response_model=PurgeUserDataResponse) async def purge_user_data( body: PurgeUserDataRequest, current_user: CurrentUserDep, service: UserService = Depends(get_user_service), object_storage: ObjectStorage = Depends(get_object_storage), ): """ 永久删除当前账号下的业务数据:对话与片段、记忆层、故事与插图意图、书籍与章节(含图片任务行)、 回忆录状态、订单记录、刷新令牌;并清理会话 Redis 历史、任务追踪与相关分布式锁 key; 对 memory_sources / memoir_images / 关联 Asset 中记录的 storage_key 尽力删除对象存储对象。 保留 users 表中的账号与登录字段(手机号、密码等),并清空出生年/出生地/成长地/职业等档案字段。 口令见请求体 schema 说明。 """ return await service.purge_all_user_data( current_user.id, confirmation=body.confirmation, object_storage=object_storage, ) @router.post("/test-subscription", response_model=TestSubscriptionResponse) async def test_subscription( body: TestSubscriptionRequest, current_user: CurrentUserDep, service: UserService = Depends(get_user_service), ): if not settings.enable_test_subscription: raise NotFoundError("测试订阅功能未开放") if body.action == "activate" and body.plan_id not in ("pro", "pro_plus"): raise BadRequestError("plan_id 仅支持 pro 或 pro_plus") return await service.toggle_test_subscription( current_user.id, body.action, body.plan_id ) @feedback_router.post( "", response_model=FeedbackResponse, status_code=status.HTTP_201_CREATED ) async def submit_feedback( request: SubmitFeedbackRequest, current_user: CurrentUserDep, ): """提交用户反馈。用户可通过此接口提交反馈意见或联系客服。""" feedback_id = str(uuid.uuid4()) logger.info( "用户反馈已提交 feedback_id={} user_id={} content_len={} has_contact={}", feedback_id, current_user.id, len(request.content or ""), bool(request.contact and str(request.contact).strip()), ) logger.debug( "用户反馈详情: feedback_id={} user_id={} content={} contact={}", feedback_id, current_user.id, request.content, request.contact, ) return FeedbackResponse( id=feedback_id, message="反馈已提交,我们会尽快处理", )