feat: 统一生产初始化流程并保护系统管理员
This commit is contained in:
@@ -51,6 +51,16 @@ async def update_user(
|
||||
db_user = await user_crud.get_by_id(db, user_id)
|
||||
if not db_user:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="用户不存在")
|
||||
if user_crud.is_protected_admin_user(db_user):
|
||||
if user_in.email is not None and user_in.email != db_user.email:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="系统管理员邮箱不允许修改")
|
||||
if user_in.role is not None and user_in.role != "ADMIN":
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="系统管理员角色不允许修改")
|
||||
requested_status = user_in.status
|
||||
if user_in.is_active is not None:
|
||||
requested_status = "ACTIVE" if user_in.is_active else "DISABLED"
|
||||
if requested_status is not None and requested_status != "ACTIVE":
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="系统管理员不允许停用")
|
||||
if db_user.role.value == "ADMIN":
|
||||
requested_role = user_in.role
|
||||
requested_status = user_in.status
|
||||
@@ -75,6 +85,8 @@ async def delete_user(
|
||||
db_user = await user_crud.get_by_id(db, user_id)
|
||||
if not db_user:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="用户不存在")
|
||||
if user_crud.is_protected_admin_user(db_user):
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="系统管理员账号不允许删除")
|
||||
if db_user.id == current_user.id:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="不允许删除自己")
|
||||
if db_user.role.value == "ADMIN" and db_user.status.value == "ACTIVE":
|
||||
|
||||
@@ -4,6 +4,11 @@ from typing import Literal
|
||||
from pydantic import Field
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
PROTECTED_ADMIN_EMAIL = "admin@huapont.cn"
|
||||
PROTECTED_ADMIN_DEFAULT_PASSWORD = "admin123"
|
||||
PROTECTED_ADMIN_FULL_NAME = "System Admin"
|
||||
PROTECTED_ADMIN_DEPARTMENT = "SYSTEM"
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
|
||||
|
||||
@@ -107,6 +107,7 @@ async def delete(db: AsyncSession, study_id: uuid.UUID) -> None:
|
||||
from app.models.finance_contract import FinanceContract
|
||||
from app.models.finance_special import FinanceSpecial
|
||||
from app.models.contract_fee import ContractFee
|
||||
from app.models.contract_fee_payment import ContractFeePayment
|
||||
from app.models.special_expense import SpecialExpense
|
||||
from app.models.milestone import Milestone
|
||||
from app.models.document import Document
|
||||
@@ -157,6 +158,13 @@ async def delete(db: AsyncSession, study_id: uuid.UUID) -> None:
|
||||
|
||||
# 9. 删除财务相关
|
||||
await db.execute(sa_delete(SpecialExpense).where(SpecialExpense.project_id == study_id))
|
||||
await db.execute(
|
||||
sa_delete(ContractFeePayment).where(
|
||||
ContractFeePayment.contract_fee_id.in_(
|
||||
select(ContractFee.id).where(ContractFee.project_id == study_id)
|
||||
)
|
||||
)
|
||||
)
|
||||
await db.execute(sa_delete(ContractFee).where(ContractFee.project_id == study_id))
|
||||
await db.execute(sa_delete(FinanceSpecial).where(FinanceSpecial.study_id == study_id))
|
||||
await db.execute(sa_delete(FinanceContract).where(FinanceContract.study_id == study_id))
|
||||
|
||||
@@ -4,6 +4,12 @@ from typing import Sequence
|
||||
from sqlalchemy import delete, func, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.config import (
|
||||
PROTECTED_ADMIN_DEFAULT_PASSWORD,
|
||||
PROTECTED_ADMIN_DEPARTMENT,
|
||||
PROTECTED_ADMIN_EMAIL,
|
||||
PROTECTED_ADMIN_FULL_NAME,
|
||||
)
|
||||
from app.core.security import hash_password
|
||||
from app.models.study_member import StudyMember
|
||||
from app.models.user import User, UserRole, UserStatus
|
||||
@@ -15,6 +21,14 @@ async def get_by_email(db: AsyncSession, email: str) -> User | None:
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
def is_protected_admin_email(email: str | None) -> bool:
|
||||
return (email or "").strip().lower() == PROTECTED_ADMIN_EMAIL
|
||||
|
||||
|
||||
def is_protected_admin_user(user: User | None) -> bool:
|
||||
return user is not None and is_protected_admin_email(user.email)
|
||||
|
||||
|
||||
async def get_by_id(db: AsyncSession, user_id: uuid.UUID) -> User | None:
|
||||
result = await db.execute(select(User).where(User.id == user_id))
|
||||
return result.scalar_one_or_none()
|
||||
@@ -90,17 +104,17 @@ async def list_users_by_status(
|
||||
return result.scalars().all()
|
||||
|
||||
|
||||
async def ensure_admin_exists(db: AsyncSession, *, default_password: str = "admin123") -> None:
|
||||
result = await db.execute(select(User).where(User.role == UserRole.ADMIN))
|
||||
async def ensure_admin_exists(db: AsyncSession, *, default_password: str = PROTECTED_ADMIN_DEFAULT_PASSWORD) -> None:
|
||||
result = await db.execute(select(User).where(User.email == PROTECTED_ADMIN_EMAIL))
|
||||
admin = result.scalar_one_or_none()
|
||||
if admin:
|
||||
return
|
||||
new_admin = User(
|
||||
email="admin@example.com",
|
||||
email=PROTECTED_ADMIN_EMAIL,
|
||||
password_hash=hash_password(default_password),
|
||||
full_name="System Admin",
|
||||
full_name=PROTECTED_ADMIN_FULL_NAME,
|
||||
role=UserRole.ADMIN,
|
||||
department="SYSTEM",
|
||||
department=PROTECTED_ADMIN_DEPARTMENT,
|
||||
status=UserStatus.ACTIVE,
|
||||
)
|
||||
db.add(new_admin)
|
||||
|
||||
Reference in New Issue
Block a user