diff --git a/app/api/v1/endpoints/article.py b/app/api/v1/endpoints/article.py index 8ee6a8d..4e131d2 100644 --- a/app/api/v1/endpoints/article.py +++ b/app/api/v1/endpoints/article.py @@ -40,19 +40,16 @@ async def get_self_folders(page_number: Optional[int] = Query(None, ge=1), page_ # 获取用户id user_id = user.get("id") - # 数据库查询 - folders = await crud_get_self_folders(user_id, page_number, page_size, db) - - # 返回结果 + total_num, folders = await crud_get_self_folders(user_id, page_number, page_size, db) result = [{"folder_id": folder.id, "folder_name": folder.name} for folder in folders] - return {"result": result} + return {"total_num": total_num, "result": result} @router.get("/getArticlesInFolder", response_model="dict") async def get_articles_in_folder(folder_id: int = Query(...), page_number: Optional[int] = Query(None, ge=1), page_size: Optional[int] = Query(None, ge=1), db: AsyncSession = Depends(get_db)): - articles = await crud_get_articles_in_folder(folder_id, page_number, page_size, db) + total_num, articles = await crud_get_articles_in_folder(folder_id, page_number, page_size, db) result = [{"article_id": article.id, "article_name": article.name} for article in articles] - return {"result": result} + return {"total_num": total_num, "result": result} @router.post("/selfCreateFolder", response_model="dict") async def self_create_folder(model: SelfCreateFolder, db: AsyncSession = Depends(get_db), user: dict = Depends(get_current_user)): diff --git a/app/api/v1/endpoints/group.py b/app/api/v1/endpoints/group.py new file mode 100644 index 0000000..56f850e --- /dev/null +++ b/app/api/v1/endpoints/group.py @@ -0,0 +1,46 @@ +from fastapi import APIRouter, Query, Body, UploadFile, File, Depends, HTTPException +from sqlalchemy.ext.asyncio import AsyncSession +import os + +from app.utils.get_db import get_db +from app.utils.auth import get_current_user +from app.curd.group import crud_create, crud_apply_to_enter, crud_get_applications, crud_reply_to_enter +from app.schemas.group import ApplyToEnter + +router = APIRouter() + +@router.post("/create", response_model=dict) +async def create(group_name: str = Query(...), group_desc: str = Query(...), group_avatar: UploadFile | None = File(None) + , db: AsyncSession = Depends(get_db), user: dict = Depends(get_current_user)): + if len(group_name) > 30: + raise HTTPException(status_code=405, detail="Invalid group name, longer than 30") + if len(group_desc) > 200: + raise HTTPException(status_code=405, detail="Invalid group description, longer than 200") + group_id = await crud_create(user.get("id"), group_name, group_desc, db) + if group_avatar: + os.makedirs("/lhcos-data/group-avatar", exist_ok=True) + ext = os.path.splitext(group_avatar.filename)[1] + path = os.path.join("/lhcos-data/group-avatar", f"{group_id}{ext}") + with open(path, "wb") as f: + content = await group_avatar.read() + f.write(content) + return {"msg": "Group created successfully"} + +@router.post("/applyToEnter", response_model=dict) +async def apply_to_enter(model: ApplyToEnter, db: AsyncSession = Depends(get_db), user: dict = Depends(get_current_user)): + group_id = model.group_id + user_id = user.get("id") + await crud_apply_to_enter(user_id, group_id, db) + return {"msg": "Application sent successfully"} + +@router.get("/getApplications", response_model=dict) +async def get_applications(group_id: int = Query(...), db: AsyncSession = Depends(get_db)): + users = await crud_get_applications(group_id, db) + return {"users": users} + +@router.post("/replyToEnter", response_model=dict) +async def reply_to_enter(user_id: int = Body(...), group_id: int = Body(...), reply: int = Body(...), db: AsyncSession = Depends(get_db)): + if reply != 0 and reply != 1: + raise HTTPException(status_code=405, detail="Wrong parameter, reply should be either 0 or 1") + msg = await crud_reply_to_enter(user_id, group_id, reply, db) + return {"msg": msg} \ No newline at end of file diff --git a/app/curd/article.py b/app/curd/article.py index d3c6b90..ccf92a9 100644 --- a/app/curd/article.py +++ b/app/curd/article.py @@ -13,23 +13,31 @@ async def crud_upload_to_self_folder(name: str, folder_id: int, db: AsyncSession async def crud_get_self_folders(user_id: int, page_number: int, page_size: int, db: AsyncSession): query = select(Folder).where(Folder.user_id == user_id, Folder.visible == True).order_by(Folder.id.desc()) + count_query = select(func.count()).select_from(query.subquery()) + count_result = await db.execute(count_query) + total_num = count_result.scalar() + if page_number and page_size: offset = (page_number - 1) * page_size query = query.offset(offset).limit(page_size) - result = await db.execute(query) folders = result.scalars().all() - return folders + + return total_num, folders async def crud_get_articles_in_folder(folder_id: int, page_number: int, page_size: int, db: AsyncSession): query = select(Article).where(Article.folder_id == folder_id, Article.visible == True).order_by(Article.id.desc()) + count_query = select(func.count()).select_from(query.subquery()) + count_result = await db.execute(count_query) + total_num = count_result.scalar() + if page_number and page_size: offset = (page_number - 1) * page_size query = query.offset(offset).limit(page_size) - result = await db.execute(query) articles = result.scalars().all() - return articles + + return total_num, articles async def crud_self_create_folder(name: str, user_id: int, db: AsyncSession): new_folder = Folder(name=name, user_id=user_id) diff --git a/app/curd/group.py b/app/curd/group.py new file mode 100644 index 0000000..4c8382d --- /dev/null +++ b/app/curd/group.py @@ -0,0 +1,58 @@ +from fastapi import HTTPException +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.exc import IntegrityError +from sqlalchemy import select, insert, delete +from app.models.model import User, Group, Folder, Article, Note, Tag, user_group, enter_application + +async def crud_create(leader: int, name: str, description: str, db: AsyncSession): + new_group = Group(leader=leader, name=name, description=description) + db.add(new_group) + await db.commit() + await db.refresh(new_group) + return new_group.id + +async def crud_apply_to_enter(user_id: int, group_id: int, db: AsyncSession): + # 是否已经在组织中 + query = select(user_group).where(user_group.c.user_id == user_id, user_group.c.group_id == group_id) + result = await db.execute(query) + existing = result.first() + if existing: + raise HTTPException(status_code=405, detail="Already in the group") + query = select(Group).where(Group.id == group_id) + result = await db.execute(query) + group = result.scalar_one_or_none() + if group.leader == user_id: + raise HTTPException(status_code=405, detail="Already in the group") + + # 插入申请表,若已存在申请则抛出异常 + query = insert(enter_application).values(user_id=user_id, group_id=group_id) + try: + await db.execute(query) + await db.commit() + except IntegrityError: + await db.rollback() + raise HTTPException(status_code=405, detail="Don't apply repeatedly") + +async def crud_get_applications(group_id: int, db: AsyncSession): + query = select(User.id, User.username).where(User.id.in_( + select(enter_application.c.user_id).where(enter_application.c.group_id == group_id) + )) + result = await db.execute(query) + users = result.all() + return [{"user_id": user.id, "user_name": user.username} for user in users] + +async def crud_reply_to_enter(user_id: int, group_id: int, reply: int, db: AsyncSession): + # 答复后,需要从待处理申请的表中删除表项 + query = delete(enter_application).where(enter_application.c.user_id == user_id, enter_application.c.group_id == group_id) + result = await db.execute(query) + if result.rowcount == 0: # 如果没有删除任何行,说明不存在该项 + raise HTTPException(status_code=405, detail="Application is not existed or already handled") + await db.commit() + + if reply == 1: + new_relation = insert(user_group).values(user_id=user_id, group_id=group_id) + await db.execute(new_relation) + await db.commit() + return "Add new member successfully" + + return "Refuse the application successfully" diff --git a/app/routers/router.py b/app/routers/router.py index 325027f..eee09a0 100644 --- a/app/routers/router.py +++ b/app/routers/router.py @@ -6,6 +6,7 @@ from app.api.v1.endpoints.aichat import router as aichat_router from app.api.v1.endpoints.article import router as article_router from app.api.v1.endpoints.articleDB import router as articleDB_router +from app.api.v1.endpoints.group import router as group_router def include_auth_router(app): app.include_router(auth_router, prefix="/public", tags=["auth"]) @@ -25,10 +26,14 @@ def include_article_router(app): def include_articleDB_router(app): app.include_router(articleDB_router, prefix="/database", tags=["articleDB"], dependencies=[Depends(get_current_user)]) +def include_group_router(app): + app.include_router(group_router, prefix="/group", tags=["group"], dependencies=[Depends(get_current_user)]) + def include_routers(app): include_auth_router(app) include_note_router(app) include_user_router(app) include_aichat_router(app) include_article_router(app) - include_articleDB_router(app) \ No newline at end of file + include_articleDB_router(app) + include_group_router(app) \ No newline at end of file diff --git a/app/schemas/group.py b/app/schemas/group.py new file mode 100644 index 0000000..dd69fe7 --- /dev/null +++ b/app/schemas/group.py @@ -0,0 +1,4 @@ +from pydantic import BaseModel + +class ApplyToEnter(BaseModel): + group_id: int \ No newline at end of file