This commit is contained in:
aaron 2025-04-09 17:33:02 +08:00
parent 10866b1cbb
commit c86bf3b9a3
10 changed files with 156 additions and 261 deletions

View File

@ -2,12 +2,12 @@ from fastapi import APIRouter
from app.api.v1.endpoints import router as endpoints_router from app.api.v1.endpoints import router as endpoints_router
from app.api.v1.users import router as users_router from app.api.v1.users import router as users_router
from app.api.v1.auth import router as auth_router from app.api.v1.auth import router as auth_router
from app.api.v1.user_images import router as user_images_router from app.api.v1.person_images import router as person_images_router
from app.api.v1.clothing import router as clothing_router from app.api.v1.clothing import router as clothing_router
api_router = APIRouter() api_router = APIRouter()
api_router.include_router(endpoints_router, prefix="") api_router.include_router(endpoints_router, prefix="")
api_router.include_router(users_router, prefix="/users") api_router.include_router(users_router, prefix="/users")
api_router.include_router(auth_router, prefix="/auth") api_router.include_router(auth_router, prefix="/auth")
api_router.include_router(user_images_router, prefix="/user-images") api_router.include_router(person_images_router, prefix="/person-images")
api_router.include_router(clothing_router, prefix="/clothing") api_router.include_router(clothing_router, prefix="/clothing")

View File

@ -0,0 +1,86 @@
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from typing import List
from app.api import deps
from app.schemas.person_image import (
PersonImage,
PersonImageCreate,
PersonImageUpdate
)
from app.services import person_image as user_image_service
router = APIRouter()
@router.get("", response_model=List[PersonImage])
async def get_person_images(
db: AsyncSession = Depends(deps.get_db),
current_user = Depends(deps.get_current_user),
skip: int = 0,
limit: int = 100
):
"""获取当前用户的所有人物形象"""
return await user_image_service.get_person_images_by_user(
db=db,
user_id=current_user.id,
skip=skip,
limit=limit
)
@router.post("", response_model=PersonImage, status_code=status.HTTP_201_CREATED)
async def create_person_image(
*,
db: AsyncSession = Depends(deps.get_db),
current_user = Depends(deps.get_current_user),
image_in: PersonImageCreate
):
"""创建新的人物形象"""
image_in.user_id = current_user.id
return await user_image_service.create_person_image(db=db, image=image_in)
@router.put("/{image_id}", response_model=PersonImage)
async def update_person_image(
*,
db: AsyncSession = Depends(deps.get_db),
current_user = Depends(deps.get_current_user),
image_id: int,
image_in: PersonImageUpdate
):
"""更新人物形象"""
image = await user_image_service.get_person_image(db=db, image_id=image_id)
if not image:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="人物形象不存在"
)
if image.user_id != current_user.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="没有权限修改此人物形象"
)
return await user_image_service.update_person_image(
db=db,
image_id=image_id,
image=image_in
)
@router.delete("/{image_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_person_image(
*,
db: AsyncSession = Depends(deps.get_db),
current_user = Depends(deps.get_current_user),
image_id: int
):
"""删除人物形象"""
image = await user_image_service.get_person_image(db=db, image_id=image_id)
if not image:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="人物形象不存在"
)
if image.user_id != current_user.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="没有权限删除此人物形象"
)
await user_image_service.delete_person_image(db=db, image_id=image_id)
return None

View File

@ -1,119 +0,0 @@
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from typing import List
from app.schemas.user_image import UserImage, UserImageCreate, UserImageUpdate, UserImageWithUser
from app.services import user_image as user_image_service
from app.db.database import get_db
from app.api.deps import get_current_user
from app.models.users import User as UserModel
from app.core.exceptions import BusinessError
from app.schemas.response import StandardResponse
import logging
# 创建日志记录器
logger = logging.getLogger(__name__)
router = APIRouter()
@router.post("/", tags=["user-images"])
async def create_user_image(
image: UserImageCreate,
current_user: UserModel = Depends(get_current_user),
db: AsyncSession = Depends(get_db)
):
"""
创建用户形象图片
需要JWT令牌认证
"""
# 设置用户ID
image.user_id = current_user.id
result = await user_image_service.create_user_image(db=db, image=image)
return StandardResponse(code=200, data=result)
@router.get("/", tags=["user-images"])
async def read_user_images(
skip: int = Query(0, ge=0),
limit: int = Query(100, ge=1, le=100),
current_user: UserModel = Depends(get_current_user),
db: AsyncSession = Depends(get_db)
):
"""
获取当前用户的所有形象图片
需要JWT令牌认证
"""
images = await user_image_service.get_user_images_by_user(
db,
user_id=current_user.id,
skip=skip,
limit=limit
)
return StandardResponse(code=200, data=images)
@router.get("/{image_id}", tags=["user-images"])
async def read_image(
image_id: int,
current_user: UserModel = Depends(get_current_user),
db: AsyncSession = Depends(get_db)
):
"""
获取指定形象图片
需要JWT令牌认证
"""
image = await user_image_service.get_user_image(db, image_id=image_id)
if image is None:
raise BusinessError("图片不存在", code=404)
if image.user_id != current_user.id:
raise BusinessError("没有权限访问此图片", code=403)
return StandardResponse(code=200, data=image)
@router.put("/{image_id}", tags=["user-images"])
async def update_image(
image_id: int,
image: UserImageUpdate,
current_user: UserModel = Depends(get_current_user),
db: AsyncSession = Depends(get_db)
):
"""
更新形象图片
需要JWT令牌认证
"""
# 检查图片是否存在且属于当前用户
db_image = await user_image_service.get_user_image(db, image_id=image_id)
if db_image is None:
raise BusinessError("图片不存在", code=404)
if db_image.user_id != current_user.id:
raise BusinessError("没有权限更新此图片", code=403)
updated_image = await user_image_service.update_user_image(
db=db,
image_id=image_id,
image=image
)
return StandardResponse(code=200, data=updated_image)
@router.delete("/{image_id}", tags=["user-images"])
async def delete_image(
image_id: int,
current_user: UserModel = Depends(get_current_user),
db: AsyncSession = Depends(get_db)
):
"""
删除形象图片
需要JWT令牌认证
"""
# 检查图片是否存在且属于当前用户
db_image = await user_image_service.get_user_image(db, image_id=image_id)
if db_image is None:
raise BusinessError("图片不存在", code=404)
if db_image.user_id != current_user.id:
raise BusinessError("没有权限删除此图片", code=403)
deleted_image = await user_image_service.delete_user_image(db=db, image_id=image_id)
return StandardResponse(code=200, data=deleted_image)

View File

@ -1,7 +1,7 @@
import asyncio import asyncio
from app.db.database import Base, engine from app.db.database import Base, engine
from app.models.users import User from app.models.users import User
from app.models.user_images import UserImage from app.models.person_images import PersonImage
from app.models.clothing import ClothingCategory, Clothing from app.models.clothing import ClothingCategory, Clothing
# 创建所有表格 # 创建所有表格

View File

@ -0,0 +1,13 @@
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey
from sqlalchemy.sql import func
from app.db.database import Base
class PersonImage(Base):
"""人物形象数据模型"""
__tablename__ = "person_images"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
image_url = Column(String(255), nullable=False)
is_default = Column(Boolean, default=False)
create_time = Column(DateTime(timezone=True), server_default=func.now())

View File

@ -1,20 +0,0 @@
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Boolean
from sqlalchemy.sql import func
from sqlalchemy.orm import relationship
from app.db.database import Base
class UserImage(Base):
"""用户个人形象库模型"""
__tablename__ = "user_images"
id = Column(Integer, primary_key=True, autoincrement=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True, comment="用户ID")
image_url = Column(String(500), nullable=False, comment="图片URL")
is_default = Column(Boolean, default=False, nullable=False, comment="是否为默认形象")
create_time = Column(DateTime, default=func.now(), comment="创建时间")
# 关系
user = relationship("User", back_populates="images")
def __repr__(self):
return f"<UserImage(id={self.id}, user_id={self.user_id})>"

View File

@ -0,0 +1,26 @@
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
class PersonImageBase(BaseModel):
"""人物形象基础模型"""
image_url: str
is_default: bool = False
class PersonImageCreate(PersonImageBase):
"""创建人物形象请求模型"""
pass
class PersonImageUpdate(PersonImageBase):
"""更新人物形象请求模型"""
image_url: Optional[str] = None
is_default: Optional[bool] = None
class PersonImage(PersonImageBase):
"""人物形象响应模型"""
id: int
user_id: int
create_time: datetime
class Config:
from_attributes = True

View File

@ -1,38 +0,0 @@
from pydantic import BaseModel, Field
from typing import Optional
from datetime import datetime
from app.schemas.user import User
class UserImageBase(BaseModel):
"""用户形象基础模式"""
image_url: str = Field(..., description="图片URL")
is_default: bool = Field(False, description="是否为默认形象")
class UserImageCreate(UserImageBase):
"""创建用户形象请求"""
user_id: Optional[int] = Field(None, description="用户ID可选通常由系统设置")
class UserImageInDB(UserImageBase):
"""数据库中的用户形象数据"""
id: int
user_id: int
create_time: datetime
class Config:
from_attributes = True
class UserImage(UserImageInDB):
"""用户形象响应模式"""
pass
class UserImageUpdate(BaseModel):
"""更新用户形象请求"""
image_url: Optional[str] = Field(None, description="图片URL")
is_default: Optional[bool] = Field(None, description="是否为默认形象")
class UserImageWithUser(UserImage):
"""包含用户信息的用户形象响应"""
user: Optional[User] = Field(None, description="用户信息")
class Config:
from_attributes = True

View File

@ -1,36 +1,36 @@
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select from sqlalchemy.future import select
from sqlalchemy import delete, update from sqlalchemy import delete, update
from app.models.user_images import UserImage from app.models.person_images import PersonImage
from app.schemas.user_image import UserImageCreate, UserImageUpdate from app.schemas.person_image import PersonImageCreate, PersonImageUpdate
from typing import List, Optional from typing import List, Optional
async def get_user_image(db: AsyncSession, image_id: int): async def get_person_image(db: AsyncSession, image_id: int):
"""获取单个用户形象""" """获取单个人物形象"""
result = await db.execute(select(UserImage).filter(UserImage.id == image_id)) result = await db.execute(select(PersonImage).filter(PersonImage.id == image_id))
return result.scalars().first() return result.scalars().first()
async def get_user_images_by_user(db: AsyncSession, user_id: int, skip: int = 0, limit: int = 100): async def get_person_images_by_user(db: AsyncSession, user_id: int, skip: int = 0, limit: int = 100):
"""获取用户的所有形象图片""" """获取用户的所有人物形象图片"""
result = await db.execute( result = await db.execute(
select(UserImage) select(PersonImage)
.filter(UserImage.user_id == user_id) .filter(PersonImage.user_id == user_id)
.order_by(UserImage.create_time.desc()) .order_by(PersonImage.create_time.desc())
.offset(skip) .offset(skip)
.limit(limit) .limit(limit)
) )
return result.scalars().all() return result.scalars().all()
async def create_user_image(db: AsyncSession, image: UserImageCreate): async def create_person_image(db: AsyncSession, image: PersonImageCreate):
"""创建用户形象 """创建人物形象
image: 包含user_id的UserImageCreate对象 image: 包含user_id的PersonImageCreate对象
""" """
# 如果设置为默认形象,先重置用户的所有形象为非默认 # 如果设置为默认形象,先重置用户的所有形象为非默认
if image.is_default and image.user_id: if image.is_default and image.user_id:
await reset_default_images(db, image.user_id) await reset_default_images(db, image.user_id)
db_image = UserImage( db_image = PersonImage(
user_id=image.user_id, user_id=image.user_id,
image_url=image.image_url, image_url=image.image_url,
is_default=image.is_default is_default=image.is_default
@ -40,9 +40,9 @@ async def create_user_image(db: AsyncSession, image: UserImageCreate):
await db.refresh(db_image) await db.refresh(db_image)
return db_image return db_image
async def update_user_image(db: AsyncSession, image_id: int, image: UserImageUpdate): async def update_person_image(db: AsyncSession, image_id: int, image: PersonImageUpdate):
"""更新用户形象""" """更新人物形象"""
db_image = await get_user_image(db, image_id) db_image = await get_person_image(db, image_id)
if not db_image: if not db_image:
return None return None
@ -62,33 +62,33 @@ async def update_user_image(db: AsyncSession, image_id: int, image: UserImageUpd
await db.refresh(db_image) await db.refresh(db_image)
return db_image return db_image
async def delete_user_image(db: AsyncSession, image_id: int): async def delete_person_image(db: AsyncSession, image_id: int):
"""删除用户形象""" """删除人物形象"""
db_image = await get_user_image(db, image_id) db_image = await get_person_image(db, image_id)
if db_image: if db_image:
await db.delete(db_image) await db.delete(db_image)
await db.commit() await db.commit()
return db_image return db_image
async def delete_user_images(db: AsyncSession, user_id: int): async def delete_person_images(db: AsyncSession, user_id: int):
"""删除用户所有形象图片""" """删除用户所有人物形象图片"""
stmt = delete(UserImage).where(UserImage.user_id == user_id) stmt = delete(PersonImage).where(PersonImage.user_id == user_id)
await db.execute(stmt) await db.execute(stmt)
await db.commit() await db.commit()
return True return True
async def reset_default_images(db: AsyncSession, user_id: int): async def reset_default_images(db: AsyncSession, user_id: int):
"""重置用户所有形象为非默认""" """重置用户所有形象为非默认"""
stmt = update(UserImage).where( stmt = update(PersonImage).where(
UserImage.user_id == user_id, PersonImage.user_id == user_id,
UserImage.is_default == True PersonImage.is_default == True
).values(is_default=False) ).values(is_default=False)
await db.execute(stmt) await db.execute(stmt)
async def get_default_image(db: AsyncSession, user_id: int): async def get_default_image(db: AsyncSession, user_id: int):
"""获取用户的默认形象""" """获取用户的默认形象"""
result = await db.execute( result = await db.execute(
select(UserImage) select(PersonImage)
.filter(UserImage.user_id == user_id, UserImage.is_default == True) .filter(PersonImage.user_id == user_id, PersonImage.is_default == True)
) )
return result.scalars().first() return result.scalars().first()

53
main.py
View File

@ -1,53 +0,0 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.core.config import settings
from app.api.v1.api import api_router
from app.core.middleware import add_response_middleware
from app.core.exceptions import add_exception_handlers
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app: FastAPI):
# 在应用启动时执行
from app.db.init_db import init_db
await init_db()
yield
# 在应用关闭时执行
# 清理代码可以放在这里
app = FastAPI(
title=settings.PROJECT_NAME,
description=settings.PROJECT_DESCRIPTION,
version=settings.PROJECT_VERSION,
lifespan=lifespan
)
# 配置CORS
app.add_middleware(
CORSMiddleware,
allow_origins=settings.BACKEND_CORS_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 添加响应中间件
add_response_middleware(app)
# 添加异常处理器
add_exception_handlers(app)
# 包含API路由
app.include_router(api_router, prefix=settings.API_V1_STR)
@app.get("/")
async def root():
return {"message": "欢迎使用美搭Meida API服务"}
@app.get("/health")
async def health_check():
return {"status": "healthy"}
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)