2026-05-09 05:59:46 +00:00
|
|
|
from collections.abc import Generator
|
|
|
|
|
from dataclasses import dataclass
|
|
|
|
|
from typing import Annotated
|
|
|
|
|
|
|
|
|
|
from fastapi import Depends, Header, HTTPException, status
|
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
|
|
|
|
|
|
from app.db.session import get_session_factory
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_db() -> Generator[Session, None, None]:
|
|
|
|
|
db = get_session_factory()()
|
|
|
|
|
try:
|
|
|
|
|
yield db
|
|
|
|
|
finally:
|
|
|
|
|
db.close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(slots=True)
|
2026-05-20 09:36:01 +08:00
|
|
|
class CurrentUserContext:
|
|
|
|
|
username: str
|
|
|
|
|
name: str
|
|
|
|
|
role_codes: list[str]
|
|
|
|
|
is_admin: bool
|
|
|
|
|
department_name: str = ""
|
2026-05-09 05:59:46 +00:00
|
|
|
|
|
|
|
|
|
2026-05-11 05:18:16 +00:00
|
|
|
def get_current_user(
|
|
|
|
|
x_auth_username: Annotated[
|
|
|
|
|
str | None,
|
|
|
|
|
Header(description="当前登录用户名。知识库接口至少需要提供用户名或姓名。"),
|
|
|
|
|
] = None,
|
|
|
|
|
x_auth_name: Annotated[
|
|
|
|
|
str | None,
|
|
|
|
|
Header(description="当前登录人展示姓名。未传时默认回退到用户名。"),
|
|
|
|
|
] = None,
|
|
|
|
|
x_auth_role_codes: Annotated[
|
|
|
|
|
str | None,
|
|
|
|
|
Header(description="角色编码列表,多个角色使用英文逗号分隔,例如 `manager,finance`。"),
|
|
|
|
|
] = None,
|
|
|
|
|
x_auth_is_admin: Annotated[
|
|
|
|
|
str | None,
|
|
|
|
|
Header(description="是否管理员,支持 `true/false/1/0`。"),
|
|
|
|
|
] = None,
|
2026-05-20 09:36:01 +08:00
|
|
|
x_auth_department: Annotated[
|
|
|
|
|
str | None,
|
|
|
|
|
Header(description="当前登录人的所属部门。"),
|
|
|
|
|
] = None,
|
2026-05-11 05:18:16 +00:00
|
|
|
) -> CurrentUserContext:
|
2026-05-09 05:59:46 +00:00
|
|
|
role_codes = [item.strip() for item in (x_auth_role_codes or "").split(",") if item.strip()]
|
|
|
|
|
is_admin = str(x_auth_is_admin or "").strip().lower() in {"1", "true", "yes", "on"}
|
|
|
|
|
|
|
|
|
|
username = (x_auth_username or "").strip()
|
|
|
|
|
name = (x_auth_name or username).strip()
|
|
|
|
|
|
|
|
|
|
if not username and not name:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
|
|
|
detail="请先登录后再访问知识库。",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return CurrentUserContext(
|
|
|
|
|
username=username or name,
|
2026-05-20 09:36:01 +08:00
|
|
|
name=name or username,
|
|
|
|
|
role_codes=role_codes,
|
|
|
|
|
is_admin=is_admin,
|
|
|
|
|
department_name=(x_auth_department or "").strip(),
|
|
|
|
|
)
|
2026-05-09 05:59:46 +00:00
|
|
|
|
|
|
|
|
|
2026-05-18 02:53:06 +00:00
|
|
|
def require_admin_user(
|
|
|
|
|
current_user: Annotated[CurrentUserContext, Depends(get_current_user)],
|
|
|
|
|
) -> CurrentUserContext:
|
2026-05-09 05:59:46 +00:00
|
|
|
if current_user.is_admin or "manager" in current_user.role_codes:
|
|
|
|
|
return current_user
|
|
|
|
|
|
2026-05-18 02:53:06 +00:00
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
|
|
|
detail="只有管理员可以上传、删除或修改知识库文件。",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def require_rule_editor_user(
|
|
|
|
|
current_user: Annotated[CurrentUserContext, Depends(get_current_user)],
|
|
|
|
|
) -> CurrentUserContext:
|
|
|
|
|
role_codes = {item.strip() for item in current_user.role_codes}
|
|
|
|
|
if current_user.is_admin or "manager" in role_codes or "finance" in role_codes:
|
|
|
|
|
return current_user
|
|
|
|
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
|
|
|
detail="只有财务人员或高级管理人员可以编辑规则草稿。",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def require_rule_reviewer_user(
|
|
|
|
|
current_user: Annotated[CurrentUserContext, Depends(get_current_user)],
|
|
|
|
|
) -> CurrentUserContext:
|
|
|
|
|
role_codes = {item.strip() for item in current_user.role_codes}
|
|
|
|
|
if current_user.is_admin or "manager" in role_codes:
|
|
|
|
|
return current_user
|
|
|
|
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
|
|
|
detail="只有高级管理人员可以审核、发布或恢复正式规则。",
|
|
|
|
|
)
|