Step 12:后端轻量优化(API 稳定性 & 前端友好)

This commit is contained in:
Cheng Zhou
2025-12-16 20:03:05 +08:00
parent a54d7faf19
commit 5eab324c50
44 changed files with 161 additions and 14 deletions
+22
View File
@@ -0,0 +1,22 @@
from fastapi import APIRouter, Depends
from app.constants import ae, finance, imp, issue, subject, task
from app.core.deps import get_current_user
router = APIRouter(tags=["FAQ"], dependencies=[Depends(get_current_user)])
@router.get(
"/",
summary="获取前端常量字典",
description="返回各模块状态/类型常量,便于前端一次性加载。",
)
async def get_constants():
return {
"task": {"status": task.TASK_STATUS, "priority": task.TASK_PRIORITY},
"subject": {"status": subject.SUBJECT_STATUS, "visit_status": subject.VISIT_STATUS},
"ae": {"seriousness": ae.AE_SERIOUSNESS, "severity": ae.AE_SEVERITY, "status": ae.AE_STATUS},
"issue": {"category": issue.ISSUE_CATEGORY, "level": issue.ISSUE_LEVEL, "status": issue.ISSUE_STATUS},
"finance": {"status": finance.FINANCE_STATUS, "category": finance.FINANCE_CATEGORY},
"imp": {"tx_types": imp.IMP_TX_TYPES, "batch_status": imp.IMP_BATCH_STATUS},
}
+10 -2
View File
@@ -7,7 +7,9 @@ from app.core.deps import get_current_user, get_db_session
from app.crud import audit as audit_crud
from app.crud import faq_category as category_crud
from app.crud import member as member_crud
from app.schemas.common import PaginatedResponse
from app.schemas.faq import CategoryCreate, CategoryRead, CategoryUpdate
from app.utils.pagination import paginate
router = APIRouter()
@@ -25,6 +27,8 @@ def _check_permission_for_scope(study_id: uuid.UUID | None, current_user, member
"/",
response_model=CategoryRead,
status_code=status.HTTP_201_CREATED,
summary="创建 FAQ 分类",
description="创建全局或项目内 FAQ 分类,项目 PM/ADMIN 可用,全局仅 ADMIN。",
)
async def create_category(
payload: CategoryCreate,
@@ -54,7 +58,9 @@ async def create_category(
@router.get(
"/",
response_model=list[CategoryRead],
response_model=PaginatedResponse[CategoryRead],
summary="FAQ 分类列表",
description="返回全局及项目内 FAQ 分类列表,可按 study_id 过滤。",
)
async def list_categories(
study_id: uuid.UUID | None = None,
@@ -68,12 +74,14 @@ async def list_categories(
if not member and current_user.role != "ADMIN":
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not a study member")
categories = await category_crud.list_categories(db, study_id, include_global=include_global, is_active=is_active)
return [CategoryRead.model_validate(c) for c in categories]
return paginate([CategoryRead.model_validate(c) for c in categories], total=len(categories))
@router.patch(
"/{category_id}",
response_model=CategoryRead,
summary="更新 FAQ 分类",
description="更新分类名称或启停状态,项目 PM/ADMIN 或全局 ADMIN。",
)
async def update_category(
category_id: uuid.UUID,
+12 -2
View File
@@ -8,7 +8,9 @@ from app.crud import audit as audit_crud
from app.crud import faq_category as category_crud
from app.crud import faq_item as faq_crud
from app.crud import member as member_crud
from app.schemas.common import PaginatedResponse
from app.schemas.faq import FaqCreate, FaqRead, FaqUpdate
from app.utils.pagination import paginate
router = APIRouter()
@@ -26,6 +28,8 @@ def _check_write_permission(study_id: uuid.UUID | None, current_user, member_rol
"/",
response_model=FaqRead,
status_code=status.HTTP_201_CREATED,
summary="创建 FAQ",
description="创建全局或项目内 FAQ,项目 FAQ 需项目 PM/ADMIN 权限。",
)
async def create_faq(
payload: FaqCreate,
@@ -61,7 +65,9 @@ async def create_faq(
@router.get(
"/",
response_model=list[FaqRead],
response_model=PaginatedResponse[FaqRead],
summary="FAQ 列表",
description="返回全局与项目 FAQ,可按关键词、分类过滤。",
)
async def list_faqs(
study_id: uuid.UUID | None = None,
@@ -115,12 +121,14 @@ async def list_faqs(
if role != "PM":
continue
visible.append(FaqRead.model_validate(it))
return visible
return paginate(visible, total=len(visible))
@router.get(
"/{item_id}",
response_model=FaqRead,
summary="FAQ 详情",
description="获取单条 FAQ,非 PM 不能查看停用 FAQ。",
)
async def get_faq(
item_id: uuid.UUID,
@@ -147,6 +155,8 @@ async def get_faq(
@router.patch(
"/{item_id}",
response_model=FaqRead,
summary="更新 FAQ",
description="更新 FAQ 内容或启停状态,项目内需 PM/ADMIN 权限。",
)
async def update_faq(
item_id: uuid.UUID,
+2 -1
View File
@@ -1,6 +1,6 @@
from fastapi import APIRouter
from app.api.v1 import auth, users, studies, sites, members, comments, attachments, audit_logs, milestones, tasks, dashboard, subjects, visits, aes, issues, data_queries, verifications, imp_products, imp_batches, imp_inventory, imp_transactions, finance, finance_dashboard, faq_categories, faqs
from app.api.v1 import auth, users, studies, sites, members, comments, attachments, audit_logs, milestones, tasks, dashboard, subjects, visits, aes, issues, data_queries, verifications, imp_products, imp_batches, imp_inventory, imp_transactions, finance, finance_dashboard, faq_categories, faqs, constants
api_router = APIRouter()
api_router.include_router(auth.router, prefix="/auth", tags=["auth"])
@@ -28,3 +28,4 @@ api_router.include_router(finance.router, prefix="/studies/{study_id}/finance",
api_router.include_router(finance_dashboard.router, prefix="/studies/{study_id}/finance", tags=["finance"])
api_router.include_router(faq_categories.router, prefix="/faqs/categories", tags=["faq-categories"])
api_router.include_router(faqs.router, prefix="/faqs/items", tags=["faqs"])
api_router.include_router(constants.router, prefix="/constants", tags=["faq"])
+6 -3
View File
@@ -4,21 +4,24 @@ from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.deps import get_db_session, require_roles
from app.schemas.common import PaginatedResponse
from app.crud import user as user_crud
from app.utils.pagination import paginate
from app.schemas.user import UserCreate, UserRead, UserUpdate
router = APIRouter()
@router.get("/", response_model=list[UserRead])
@router.get("/", response_model=PaginatedResponse[UserRead], summary="用户列表", description="仅 ADMIN 可用,支持 skip/limit。")
async def list_users(
skip: int = 0,
limit: int = 100,
db: AsyncSession = Depends(get_db_session),
current_user=Depends(require_roles(["ADMIN"])),
) -> list[UserRead]:
) -> PaginatedResponse[UserRead]:
users = await user_crud.list_users(db, skip=skip, limit=limit)
return list(users)
total_users = await user_crud.list_users(db, skip=0, limit=10_000_000)
return paginate(list(users), total=len(total_users))
@router.post("/", response_model=UserRead, status_code=status.HTTP_201_CREATED)