全局中文化

This commit is contained in:
Cheng Zhou
2026-01-12 11:14:04 +08:00
parent 9021c7fe2b
commit 1338fa7e2b
103 changed files with 2443 additions and 1533 deletions
+34 -34
View File
@@ -27,12 +27,12 @@ router = APIRouter()
def _check_write_permission(study_id: uuid.UUID, current_user, member_role: str | None):
if current_user.role != "ADMIN" and member_role != "PM":
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Insufficient permissions")
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="权限不足")
def _check_create_permission(current_user, is_member: bool):
if current_user.role != "ADMIN" and not is_member:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not a study member")
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="不是该项目成员")
@router.post(
@@ -48,12 +48,12 @@ async def create_faq(
current_user=Depends(get_current_user),
) -> FaqRead:
if not payload.study_id:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="study_id is required")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="必须提供项目 ID")
cat = await category_crud.get_category(db, payload.category_id)
if not cat:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Category not found")
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="分类不存在")
if cat.study_id != payload.study_id:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Category scope mismatch")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="分类范围不匹配")
member_role = None
is_member = False
member = await member_crud.get_member(db, payload.study_id, current_user.id)
@@ -79,7 +79,7 @@ async def create_faq(
entity_type="faq_item",
entity_id=item.id,
action="CREATE_FAQ_ITEM",
detail="FAQ created",
detail="FAQ 已创建",
operator_id=current_user.id,
operator_role=current_user.role,
)
@@ -111,16 +111,16 @@ async def list_faqs(
return role
if not study_id:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="study_id is required")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="必须提供项目 ID")
if current_user.role != "ADMIN":
role = await _get_member_role(study_id)
if not role:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not a study member")
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="不是该项目成员")
if is_active is False and current_user.role != "ADMIN":
role = await _get_member_role(study_id)
if role != "PM":
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Insufficient permissions")
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="权限不足")
items = await faq_crud.list_items(
db,
@@ -158,18 +158,18 @@ async def get_faq(
) -> FaqRead:
item = await faq_crud.get_item(db, item_id)
if not item:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ not found")
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ 不存在")
if not item.study_id:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ not found")
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ 不存在")
if current_user.role != "ADMIN":
member = await member_crud.get_member(db, item.study_id, current_user.id)
if not member:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not a study member")
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="不是该项目成员")
if not item.is_active and current_user.role not in {"ADMIN"}:
member = await member_crud.get_member(db, item.study_id, current_user.id)
member_role = member.role_in_study if member else None
if member_role != "PM":
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Inactive FAQ")
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="FAQ 已停用")
return FaqRead.model_validate(item)
@@ -187,7 +187,7 @@ async def update_faq(
) -> FaqRead:
item = await faq_crud.get_item(db, item_id)
if not item:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ not found")
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ 不存在")
if item.created_by != current_user.id:
member_role = None
if item.study_id:
@@ -228,16 +228,16 @@ async def update_faq_status(
) -> FaqRead:
item = await faq_crud.get_item(db, item_id)
if not item:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ not found")
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ 不存在")
if payload.status != "RESOLVED":
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Only RESOLVED is allowed")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="仅允许设置为已解决")
if item.created_by != current_user.id and current_user.role != "ADMIN":
member_role = None
if item.study_id:
member = await member_crud.get_member(db, item.study_id, current_user.id)
member_role = member.role_in_study if member else None
if member_role != "PM":
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Insufficient permissions")
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="权限不足")
await faq_crud.set_status(db, item.id, "RESOLVED", resolved_by_confirm=True)
updated = await faq_crud.get_item(db, item_id)
return FaqRead.model_validate(updated)
@@ -257,9 +257,9 @@ async def set_best_reply(
) -> FaqRead:
item = await faq_crud.get_item(db, item_id)
if not item:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ not found")
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ 不存在")
if not item.study_id:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ not found")
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ 不存在")
is_member = False
member = await member_crud.get_member(db, item.study_id, current_user.id)
is_member = member is not None
@@ -267,9 +267,9 @@ async def set_best_reply(
if payload.best_reply_id:
reply = await reply_crud.get_reply(db, payload.best_reply_id)
if not reply or reply.faq_id != item.id:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid reply")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="回复无效")
if reply.is_deleted:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Reply deleted")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="回复已删除")
await faq_crud.set_best_reply(db, item.id, reply.id)
await faq_crud.set_status(db, item.id, "RESOLVED", resolved_by_confirm=item.resolved_by_confirm)
else:
@@ -299,11 +299,11 @@ async def list_replies(
) -> list[FaqReplyRead]:
item = await faq_crud.get_item(db, item_id)
if not item:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ not found")
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ 不存在")
if item.study_id and current_user.role != "ADMIN":
member = await member_crud.get_member(db, item.study_id, current_user.id)
if not member:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not a study member")
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="不是该项目成员")
replies = await reply_crud.list_replies(db, item_id)
reply_map = {r.id: r for r in replies}
result: list[FaqReplyRead] = []
@@ -339,11 +339,11 @@ async def create_reply(
) -> FaqReplyRead:
item = await faq_crud.get_item(db, item_id)
if not item:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ not found")
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ 不存在")
if not item.study_id:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ not found")
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ 不存在")
if not payload.content.strip():
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Reply content is required")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="回复内容不能为空")
is_member = False
member = await member_crud.get_member(db, item.study_id, current_user.id)
is_member = member is not None
@@ -352,7 +352,7 @@ async def create_reply(
if payload.quote_reply_id:
quote = await reply_crud.get_reply(db, payload.quote_reply_id)
if not quote or quote.faq_id != item.id:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid quote reply")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="引用回复无效")
reply = await reply_crud.create_reply(
db,
faq_id=item.id,
@@ -369,7 +369,7 @@ async def create_reply(
entity_type="faq_reply",
entity_id=reply.id,
action="CREATE_FAQ_REPLY",
detail="FAQ replied",
detail="FAQ 已回复",
operator_id=current_user.id,
operator_role=current_user.role,
)
@@ -400,7 +400,7 @@ async def delete_faq(
) -> None:
item = await faq_crud.get_item(db, item_id)
if not item:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ not found")
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ 不存在")
if item.created_by != current_user.id:
member_role = None
if item.study_id:
@@ -416,7 +416,7 @@ async def delete_faq(
entity_type="faq_item",
entity_id=item_id,
action="DELETE_FAQ_ITEM",
detail="FAQ deleted",
detail="FAQ 已删除",
operator_id=current_user.id,
operator_role=current_user.role,
)
@@ -436,17 +436,17 @@ async def delete_reply(
) -> None:
item = await faq_crud.get_item(db, item_id)
if not item:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ not found")
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="FAQ 不存在")
reply = await reply_crud.get_reply(db, reply_id)
if not reply or reply.faq_id != item.id:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Reply not found")
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="回复不存在")
if reply.created_by != current_user.id and current_user.role != "ADMIN":
member_role = None
if item.study_id:
member = await member_crud.get_member(db, item.study_id, current_user.id)
member_role = member.role_in_study if member else None
if member_role != "PM":
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Insufficient permissions")
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="权限不足")
if item.best_reply_id == reply.id:
await faq_crud.set_best_reply(db, item.id, None)
ref_count = await reply_crud.count_quote_references(db, reply.id)
@@ -472,7 +472,7 @@ async def delete_reply(
entity_type="faq_reply",
entity_id=reply_id,
action="DELETE_FAQ_REPLY",
detail="FAQ reply deleted",
detail="FAQ 回复已删除",
operator_id=current_user.id,
operator_role=current_user.role,
)