初步实现数据加密、权限管理、日志审计等功能

This commit is contained in:
2026-02-02 10:09:28 +08:00
parent b6b37a453b
commit 807e634318
27 changed files with 3787 additions and 59 deletions

36
app/domain/models/role.py Normal file
View File

@@ -0,0 +1,36 @@
from enum import Enum
class UserRole(str, Enum):
"""用户角色枚举"""
ADMIN = "ADMIN" # 管理员 - 完全权限
OPERATOR = "OPERATOR" # 操作员 - 可修改数据
USER = "USER" # 普通用户 - 读写权限
VIEWER = "VIEWER" # 观察者 - 仅查询权限
def __str__(self):
return self.value
@classmethod
def get_hierarchy(cls) -> dict:
"""
获取角色层级(数字越大权限越高)
"""
return {
cls.VIEWER: 1,
cls.USER: 2,
cls.OPERATOR: 3,
cls.ADMIN: 4,
}
def has_permission(self, required_role: 'UserRole') -> bool:
"""
检查当前角色是否有足够权限
Args:
required_role: 需要的最低角色
Returns:
True if has permission
"""
hierarchy = self.get_hierarchy()
return hierarchy[self] >= hierarchy[required_role]

View File

@@ -0,0 +1,48 @@
from datetime import datetime
from typing import Optional, Any
from pydantic import BaseModel, ConfigDict, Field
class AuditLogCreate(BaseModel):
"""创建审计日志"""
user_id: Optional[int] = None
username: Optional[str] = None
action: str
resource_type: Optional[str] = None
resource_id: Optional[str] = None
ip_address: Optional[str] = None
user_agent: Optional[str] = None
request_method: Optional[str] = None
request_path: Optional[str] = None
request_data: Optional[dict] = None
response_status: Optional[int] = None
error_message: Optional[str] = None
class AuditLogResponse(BaseModel):
"""审计日志响应"""
id: int
user_id: Optional[int]
username: Optional[str]
action: str
resource_type: Optional[str]
resource_id: Optional[str]
ip_address: Optional[str]
user_agent: Optional[str]
request_method: Optional[str]
request_path: Optional[str]
request_data: Optional[dict]
response_status: Optional[int]
error_message: Optional[str]
timestamp: datetime
model_config = ConfigDict(from_attributes=True)
class AuditLogQuery(BaseModel):
"""审计日志查询参数"""
user_id: Optional[int] = None
username: Optional[str] = None
action: Optional[str] = None
resource_type: Optional[str] = None
start_time: Optional[datetime] = None
end_time: Optional[datetime] = None
skip: int = Field(default=0, ge=0)
limit: int = Field(default=100, ge=1, le=1000)

View File

@@ -0,0 +1,68 @@
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, EmailStr, Field, ConfigDict
from app.domain.models.role import UserRole
# ============================================
# Request Schemas (输入)
# ============================================
class UserCreate(BaseModel):
"""用户注册"""
username: str = Field(..., min_length=3, max_length=50,
description="用户名3-50个字符")
email: EmailStr = Field(..., description="邮箱地址")
password: str = Field(..., min_length=6, max_length=100,
description="密码至少6个字符")
role: UserRole = Field(default=UserRole.USER, description="用户角色")
class UserLogin(BaseModel):
"""用户登录"""
username: str = Field(..., description="用户名或邮箱")
password: str = Field(..., description="密码")
class UserUpdate(BaseModel):
"""用户信息更新"""
email: Optional[EmailStr] = None
password: Optional[str] = Field(None, min_length=6, max_length=100)
role: Optional[UserRole] = None
is_active: Optional[bool] = None
# ============================================
# Response Schemas (输出)
# ============================================
class UserResponse(BaseModel):
"""用户信息响应(不含密码)"""
id: int
username: str
email: str
role: UserRole
is_active: bool
is_superuser: bool
created_at: datetime
updated_at: datetime
model_config = ConfigDict(from_attributes=True)
class UserInDB(UserResponse):
"""数据库中的用户(含密码哈希)"""
hashed_password: str
# ============================================
# Token Schemas
# ============================================
class Token(BaseModel):
"""JWT Token 响应"""
access_token: str
refresh_token: Optional[str] = None
token_type: str = "bearer"
expires_in: int = Field(..., description="过期时间(秒)")
class TokenPayload(BaseModel):
"""JWT Token Payload"""
sub: str = Field(..., description="用户ID或用户名")
exp: Optional[int] = None
iat: Optional[int] = None
type: str = Field(default="access", description="token类型: access 或 refresh")