update
This commit is contained in:
parent
10866b1cbb
commit
c86bf3b9a3
@ -2,12 +2,12 @@ from fastapi import APIRouter
|
||||
from app.api.v1.endpoints import router as endpoints_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.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
|
||||
|
||||
api_router = APIRouter()
|
||||
api_router.include_router(endpoints_router, prefix="")
|
||||
api_router.include_router(users_router, prefix="/users")
|
||||
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")
|
||||
|
||||
86
app/api/v1/person_images.py
Normal file
86
app/api/v1/person_images.py
Normal 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
|
||||
@ -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)
|
||||
@ -1,7 +1,7 @@
|
||||
import asyncio
|
||||
from app.db.database import Base, engine
|
||||
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
|
||||
|
||||
# 创建所有表格
|
||||
|
||||
13
app/models/person_images.py
Normal file
13
app/models/person_images.py
Normal 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())
|
||||
@ -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})>"
|
||||
26
app/schemas/person_image.py
Normal file
26
app/schemas/person_image.py
Normal 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
|
||||
@ -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
|
||||
@ -1,36 +1,36 @@
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.future import select
|
||||
from sqlalchemy import delete, update
|
||||
from app.models.user_images import UserImage
|
||||
from app.schemas.user_image import UserImageCreate, UserImageUpdate
|
||||
from app.models.person_images import PersonImage
|
||||
from app.schemas.person_image import PersonImageCreate, PersonImageUpdate
|
||||
from typing import List, Optional
|
||||
|
||||
async def get_user_image(db: AsyncSession, image_id: int):
|
||||
"""获取单个用户形象"""
|
||||
result = await db.execute(select(UserImage).filter(UserImage.id == image_id))
|
||||
async def get_person_image(db: AsyncSession, image_id: int):
|
||||
"""获取单个人物形象"""
|
||||
result = await db.execute(select(PersonImage).filter(PersonImage.id == image_id))
|
||||
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(
|
||||
select(UserImage)
|
||||
.filter(UserImage.user_id == user_id)
|
||||
.order_by(UserImage.create_time.desc())
|
||||
select(PersonImage)
|
||||
.filter(PersonImage.user_id == user_id)
|
||||
.order_by(PersonImage.create_time.desc())
|
||||
.offset(skip)
|
||||
.limit(limit)
|
||||
)
|
||||
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:
|
||||
await reset_default_images(db, image.user_id)
|
||||
|
||||
db_image = UserImage(
|
||||
db_image = PersonImage(
|
||||
user_id=image.user_id,
|
||||
image_url=image.image_url,
|
||||
is_default=image.is_default
|
||||
@ -40,9 +40,9 @@ async def create_user_image(db: AsyncSession, image: UserImageCreate):
|
||||
await db.refresh(db_image)
|
||||
return db_image
|
||||
|
||||
async def update_user_image(db: AsyncSession, image_id: int, image: UserImageUpdate):
|
||||
"""更新用户形象"""
|
||||
db_image = await get_user_image(db, image_id)
|
||||
async def update_person_image(db: AsyncSession, image_id: int, image: PersonImageUpdate):
|
||||
"""更新人物形象"""
|
||||
db_image = await get_person_image(db, image_id)
|
||||
if not db_image:
|
||||
return None
|
||||
|
||||
@ -62,33 +62,33 @@ async def update_user_image(db: AsyncSession, image_id: int, image: UserImageUpd
|
||||
await db.refresh(db_image)
|
||||
return db_image
|
||||
|
||||
async def delete_user_image(db: AsyncSession, image_id: int):
|
||||
"""删除用户形象"""
|
||||
db_image = await get_user_image(db, image_id)
|
||||
async def delete_person_image(db: AsyncSession, image_id: int):
|
||||
"""删除人物形象"""
|
||||
db_image = await get_person_image(db, image_id)
|
||||
if db_image:
|
||||
await db.delete(db_image)
|
||||
await db.commit()
|
||||
return db_image
|
||||
|
||||
async def delete_user_images(db: AsyncSession, user_id: int):
|
||||
"""删除用户所有形象图片"""
|
||||
stmt = delete(UserImage).where(UserImage.user_id == user_id)
|
||||
async def delete_person_images(db: AsyncSession, user_id: int):
|
||||
"""删除用户所有人物形象图片"""
|
||||
stmt = delete(PersonImage).where(PersonImage.user_id == user_id)
|
||||
await db.execute(stmt)
|
||||
await db.commit()
|
||||
return True
|
||||
|
||||
async def reset_default_images(db: AsyncSession, user_id: int):
|
||||
"""重置用户所有形象为非默认"""
|
||||
stmt = update(UserImage).where(
|
||||
UserImage.user_id == user_id,
|
||||
UserImage.is_default == True
|
||||
stmt = update(PersonImage).where(
|
||||
PersonImage.user_id == user_id,
|
||||
PersonImage.is_default == True
|
||||
).values(is_default=False)
|
||||
await db.execute(stmt)
|
||||
|
||||
async def get_default_image(db: AsyncSession, user_id: int):
|
||||
"""获取用户的默认形象"""
|
||||
result = await db.execute(
|
||||
select(UserImage)
|
||||
.filter(UserImage.user_id == user_id, UserImage.is_default == True)
|
||||
select(PersonImage)
|
||||
.filter(PersonImage.user_id == user_id, PersonImage.is_default == True)
|
||||
)
|
||||
return result.scalars().first()
|
||||
53
main.py
53
main.py
@ -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)
|
||||
Loading…
Reference in New Issue
Block a user