软件工程结对作业系统重构后完整代码
user_manager.py
`import re
import uuid
from typing import List, Optional, Dict
from datetime import datetime
from user import User, Role, UserAlreadyExistsError, InvalidCredentialsError, InvalidEmailError, UserNotFoundError, PermissionDeniedError
class UserManager:
"""用户管理类,负责注册、登录、密码重置等功能"""
# 邮箱格式正则表达式
EMAIL_PATTERN = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
def __init__(self):
# 内存存储用户数据
self._users = {}
def _validate_email(self, email: str) -> None:
"""验证邮箱格式"""
if not re.match(self.EMAIL_PATTERN, email):
raise InvalidEmailError("Invalid email format")
def register_user(self, username: str, email: str, password: str, role: Role = Role.USER) -> str:
"""注册新用户"""
self._validate_email(email)
if email in self._users:
raise UserAlreadyExistsError("Email already registered")
user_id = str(uuid.uuid4())
password_hash = User.hash_password(password)
user = User(
user_id=user_id,
username=username,
email=email,
password_hash=password_hash,
role=role
)
self._users[email] = user
return user_id
def login_user(self, email: str, password: str) -> User:
"""用户登录"""
if email not in self._users:
raise UserNotFoundError("User not found")
user = self._users[email]
if not user.is_active:
raise InvalidCredentialsError("Account is disabled")
if not user.verify_password(password):
raise InvalidCredentialsError("Incorrect password")
return user
def logout_user(self, email: str) -> None:
"""用户注销(由会话管理处理令牌清除)"""
if email not in self._users:
raise UserNotFoundError("User not found")
def reset_password(self, email: str) -> str:
"""模拟通过邮箱重置密码"""
if email not in self._users:
raise UserNotFoundError("User not found")
# 模拟生成重置链接
reset_token = str(uuid.uuid4())
print(f"Password reset link sent to {email}: /reset?token={reset_token}")
return reset_token
def update_password(self, email: str, new_password: str) -> None:
"""更新用户密码(模拟重置后操作)"""
if email not in self._users:
raise UserNotFoundError("User not found")
user = self._users[email]
user.password_hash = User.hash_password(new_password)
def get_user_info(self, email: str, requesting_user: Optional[User]) -> User:
"""获取用户信息"""
if email not in self._users:
raise UserNotFoundError("User not found")
user = self._users[email]
if requesting_user is not None and requesting_user.email != email and requesting_user.role != Role.ADMIN:
raise PermissionDeniedError("Permission denied")
return user
def get_user_by_id(self, user_id: str, requesting_user: Optional[User]) -> User:
"""获取用户通过用户ID"""
for user in self._users.values():
if user.user_id == user_id:
if requesting_user is not None and requesting_user.user_id != user_id and requesting_user.role != Role.ADMIN:
raise PermissionDeniedError("Permission denied")
return user
raise UserNotFoundError(f"User {user_id} not found")
def list_users(self, requesting_user: User) -> List[User]:
"""管理员查看用户列表"""
if requesting_user.role != Role.ADMIN:
raise PermissionDeniedError("Permission denied")
return list(self._users.values())
def disable_user(self, target_email: str, requesting_user: User) -> None:
"""管理员禁用用户账户"""
if requesting_user.role != Role.ADMIN:
raise PermissionDeniedError("Permission denied")
if target_email not in self._users:
raise UserNotFoundError("User not found")
self._users[target_email].is_active = False
def is_user_valid(self, user_id: str) -> bool:
"""验证用户是否存在"""
for user in self._users.values():
if user.user_id == user_id:
return user.is_active
return False
def is_admin(self, user_id: str) -> bool:
"""验证用户是否为管理员"""
for user in self._users.values():
if user.user_id == user_id and user.is_active:
return user.role == Role.ADMIN
return False
def are_users_valid(self, user_ids: List[str]) -> Dict[str, bool]:
"""验证多个用户ID是否有效"""
return {user_id: self.is_user_valid(user_id) for user_id in user_ids}
# 数据库接口(未来扩展)
def save_to_database(self):
"""抽象方法:保存用户数据到数据库"""
raise NotImplementedError("Database storage not implemented")
def load_from_database(self):
"""抽象方法:从数据库加载用户数据"""
raise NotImplementedError("Database storage not implemented")`
user.py
`from dataclasses import dataclass
from enum import Enum
from typing import Optional
import bcrypt
用户角色枚举
class Role(Enum):
ADMIN = "admin"
USER = "user"
自定义异常
class UserAlreadyExistsError(Exception):
pass
class InvalidCredentialsError(Exception):
pass
class InvalidEmailError(Exception):
pass
class UserNotFoundError(Exception):
pass
class PermissionDeniedError(Exception):
pass
用户数据模型
@dataclass
class User:
user_id: str
username: str
email: str
password_hash: bytes
role: Role
is_active: bool = True
@staticmethod
def hash_password(password: str) -> bytes:
"""加密密码"""
return bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
def verify_password(self, password: str) -> bool:
"""验证密码"""
return bcrypt.checkpw(password.encode('utf-8'), self.password_hash)`
task_query_manager.py
`from typing import List, Optional, Dict, Any
from datetime import datetime
import re
import logging
from task_manager import TaskManager, Task, TaskStatus, TaskPriority, InvalidTaskError, InvalidUserError
from user_manager import UserManager, PermissionDeniedError, UserNotFoundError, User
from user import Role
logging.basicConfig(level=logging.DEBUG)
class TaskQueryManager:
"""任务查询管理类,支持按条件查询、分页排序、任务详情查看和统计功能"""
def __init__(self, task_manager: TaskManager, user_manager: UserManager):
"""初始化查询管理器,依赖任务管理和用户管理"""
self.task_manager = task_manager
self.user_manager = user_manager
def query_tasks(
self,
caller_id: str,
status: Optional[str] = None,
priority: Optional[str] = None,
assignee_id: Optional[str] = None,
due_date_start: Optional[datetime] = None,
due_date_end: Optional[datetime] = None,
keyword: Optional[str] = None,
page: int = 1,
page_size: int = 10,
sort_by: str = "due_date",
sort_order: str = "asc"
) -> Dict[str, Any]:
"""按条件查询任务,支持分页和排序"""
# 验证调用者
if not self.user_manager.is_user_valid(caller_id):
raise InvalidUserError("Invalid caller")
# 验证分页参数
if page < 1 or page_size < 1:
raise ValueError("Page and page_size must be positive")
# 验证排序参数
if sort_by not in ["due_date", "priority"]:
raise ValueError("Invalid sort_by: must be 'due_date' or 'priority'")
if sort_order not in ["asc", "desc"]:
raise ValueError("Invalid sort_order: must be 'asc' or 'desc'")
# 验证负责人
if assignee_id:
valid_users = self.user_manager.are_users_valid([assignee_id])
if not valid_users.get(assignee_id, False):
raise InvalidUserError(f"Assignee {assignee_id} does not exist")
# 构建过滤条件
filters = {}
if status:
filters["status"] = status
if priority:
filters["priority"] = priority
if assignee_id:
filters["assignee_id"] = assignee_id
if due_date_start:
filters["due_date_start"] = due_date_start
if due_date_end:
filters["due_date_end"] = due_date_end
if keyword:
filters["keyword"] = keyword
# 获取任务(存储层过滤)
tasks = self.task_manager.query_tasks(filters)
# 权限过滤:普通用户只能查询自己创建或被分配的任务
if not self.user_manager.is_admin(caller_id):
tasks = [t for t in tasks if caller_id in t.assignees or t.creator_id == caller_id]
# 排序
if sort_by == "due_date":
tasks.sort(key=lambda t: t.due_date, reverse=(sort_order == "desc"))
elif sort_by == "priority":
tasks.sort(key=lambda t: t.priority.get_order(), reverse=(sort_order == "desc"))
# 分页
total_tasks = len(tasks)
if total_tasks == 0:
return {
"message": "No tasks found",
"tasks": [],
"total": 0,
"page": page,
"page_size": page_size,
"total_pages": 0
}
start = (page - 1) * page_size
end = start + page_size
paginated_tasks = tasks[start:end]
# 获取调用者用户信息(用于权限验证)
caller = self.user_manager.get_user_by_id(caller_id, requesting_user=None)
# 增强任务数据:添加创建者和负责人用户名
enhanced_tasks = []
for task in paginated_tasks:
task_dict = task.to_dict()
try:
creator = self.user_manager.get_user_by_id(task.creator_id, requesting_user=None)
task_dict["creator_username"] = creator.username
assignees = [
self.user_manager.get_user_by_id(uid, requesting_user=caller)
for uid in task.assignees
]
task_dict["assignee_usernames"] = sorted(user.username for user in assignees)
except UserNotFoundError as e:
logging.error(f"User not found: {e}")
task_dict["creator_username"] = "Unknown"
task_dict["assignee_usernames"] = ["Unknown" for _ in task.assignees]
enhanced_tasks.append(task_dict)
return {
"tasks": enhanced_tasks,
"total": total_tasks,
"page": page,
"page_size": page_size,
"total_pages": (total_tasks + page_size - 1) // page_size
}
def get_task_details(self, task_id: str, caller_id: str) -> Dict[str, Any]:
"""获取任务详细信息"""
# 验证调用者
if not self.user_manager.is_user_valid(caller_id):
raise InvalidUserError("Invalid caller")
# 获取任务
task = self.task_manager.storage.get_task(task_id)
if not task:
raise InvalidTaskError(f"Task {task_id} does not exist")
# 权限验证
if not self.user_manager.is_admin(caller_id) and \
caller_id not in task.assignees and caller_id != task.creator_id:
raise PermissionDeniedError("Permission denied")
# 获取调用者用户信息
caller = self.user_manager.get_user_by_id(caller_id, requesting_user=None)
logging.debug(f"Caller ID: {caller_id}, Task Creator ID: {task.creator_id}")
# 增强任务数据
task_dict = task.to_dict()
try:
creator = self.user_manager.get_user_by_id(task.creator_id, requesting_user=None)
task_dict["creator_username"] = creator.username
task_dict["assignee_usernames"] = [
self.user_manager.get_user_by_id(uid, requesting_user=caller).username
for uid in task.assignees
]
except UserNotFoundError as e:
logging.error(f"User not found: {e}")
task_dict["creator_username"] = "Unknown"
task_dict["assignee_usernames"] = ["Unknown" for _ in task.assignees]
return task_dict
def count_incomplete_tasks(self, user_id: str, caller_id: str) -> int:
"""统计未完成任务数量"""
caller = self.user_manager.get_user_by_id(caller_id, requesting_user=None)
if caller_id != user_id and caller.role != Role.ADMIN:
raise PermissionDeniedError("Only user or admin can count tasks")
filters = {}
if caller_id != user_id:
filters["assignee_id"] = user_id
todo_tasks = self.task_manager.query_tasks({**filters, "status": "todo"})
in_progress_tasks = self.task_manager.query_tasks({**filters, "status": "in_progress"})
return len(todo_tasks) + len(in_progress_tasks)`
task_manager.py
`import uuid
import datetime
from enum import Enum
from abc import ABC, abstractmethod
from typing import Dict, Any, List, Optional
import re
import logging
from user_manager import UserManager
Configure logging
logging.basicConfig(level=logging.DEBUG)
枚举定义
class TaskStatus(Enum):
TODO = "todo"
IN_PROGRESS = "in_progress"
COMPLETED = "completed"
class TaskPriority(Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
def get_order(self) -> int:
"""Return the sorting order for the priority."""
return {"low": 1, "medium": 2, "high": 3}[self.value]
异常定义
class TaskError(Exception):
pass
class InvalidDueDateError(TaskError):
pass
class PermissionDeniedError(TaskError):
pass
class InvalidUserError(TaskError):
pass
class InvalidTaskError(TaskError):
pass
任务数据结构
class Task:
def init(
self,
task_id: str,
title: str,
creator_id: str,
due_date: datetime.datetime,
description: str = "",
priority: TaskPriority = TaskPriority.MEDIUM,
status: TaskStatus = TaskStatus.TODO,
):
self.task_id = task_id
self.title = title
self.description = description
self.priority = priority
self.due_date = due_date
self.status = status
self.creator_id = creator_id
self.assignees: List[str] = []
self.created_at = datetime.datetime.now()
self.updated_at = self.created_at
self.completed_at: Optional[datetime.datetime] = None
self.archived: bool = False
self.assignment_time: Optional[datetime.datetime] = None
def to_dict(self) -> dict:
return {
"task_id": self.task_id,
"title": self.title,
"description": self.description,
"priority": self.priority.value,
"due_date": self.due_date.isoformat(),
"status": self.status.value,
"creator_id": self.creator_id,
"assignees": self.assignees,
"created_at": self.created_at.isoformat(),
"updated_at": self.updated_at.isoformat(),
"completed_at": self.completed_at.isoformat() if self.completed_at else None,
"archived": self.archived,
"assignment_time": self.assignment_time.isoformat() if self.assignment_time else None,
}
数据库接口(抽象,保留扩展)
class ITaskStorage(ABC):
@abstractmethod
def save_task(self, task: Task) -> None:
pass
@abstractmethod
def delete_task(self, task_id: str) -> None:
pass
@abstractmethod
def get_task(self, task_id: str) -> Optional[Task]:
pass
@abstractmethod
def get_all_tasks(self) -> List[Task]:
pass
@abstractmethod
def get_tasks_by_ids(self, task_ids: List[str]) -> List[Task]:
pass
内存存储实现
class MemoryTaskStorage(ITaskStorage):
def init(self):
self.tasks: dict[str, Task] = {}
def save_task(self, task: Task) -> None:
self.tasks[task.task_id] = task
def delete_task(self, task_id: str) -> None:
self.tasks.pop(task_id, None)
def get_task(self, task_id: str) -> Optional[Task]:
return self.tasks.get(task_id)
def get_all_tasks(self) -> List[Task]:
return list(self.tasks.values())
def get_tasks_by_ids(self, task_ids: List[str]) -> List[Task]:
return [self.tasks[task_id] for task_id in task_ids if task_id in self.tasks]
任务管理核心逻辑
class TaskManager:
def init(self, user_manager: UserManager, storage: ITaskStorage = None):
self.user_manager = user_manager
self.storage = storage or MemoryTaskStorage()
def create_task(
self,
title: str,
creator_id: str,
due_date: datetime.datetime,
description: str = "",
priority: str = TaskPriority.MEDIUM.value, # Default to "medium"
) -> Task:
# 验证必填字段
if not title:
raise InvalidTaskError("Title is required")
if not self.user_manager.is_user_valid(creator_id):
raise InvalidUserError(f"Creator {creator_id} does not exist")
if due_date < datetime.datetime.now():
raise InvalidDueDateError("Due date cannot be in the past")
# 验证优先级
logging.debug(f"Priority input: '{priority}'")
priority = priority.strip().lower() # Strip whitespace and normalize
logging.debug(f"Normalized priority: '{priority}'")
try:
priority_enum = TaskPriority(priority)
except ValueError as e:
logging.debug(f"Valid TaskPriority values: {[member.value for member in TaskPriority]}")
raise InvalidTaskError(f"Invalid priority: {priority}") from e
# 创建任务
task_id = str(uuid.uuid4())
task = Task(
task_id=task_id,
title=title,
creator_id=creator_id,
due_date=due_date,
description=description,
priority=priority_enum,
)
self.storage.save_task(task)
return task
def update_task(
self,
task_id: str,
caller_id: str,
title: Optional[str] = None,
description: Optional[str] = None,
priority: Optional[str] = None,
due_date: Optional[datetime.datetime] = None,
status: Optional[str] = None,
) -> None:
task = self.storage.get_task(task_id)
if not task:
raise InvalidTaskError(f"Task {task_id} does not exist")
# 权限验证
if caller_id != task.creator_id and not self.user_manager.is_admin(caller_id):
raise PermissionDeniedError("Only creator or admin can update task")
# 更新字段
if title is not None:
if not title:
raise InvalidTaskError("Title cannot be empty")
task.title = title
if description is not None:
task.description = description
if priority is not None:
try:
priority = priority.strip().lower()
task.priority = TaskPriority(priority)
except ValueError:
raise InvalidTaskError(f"Invalid priority: {priority}")
if due_date is not None:
if due_date < datetime.datetime.now():
raise InvalidDueDateError("Due date cannot be in the past")
task.due_date = due_date
if status is not None:
try:
new_status = TaskStatus(status)
task.status = new_status
if new_status == TaskStatus.COMPLETED:
task.completed_at = datetime.datetime.now()
except ValueError:
raise InvalidTaskError(f"Invalid status: {status}")
task.updated_at = datetime.datetime.now()
self.storage.save_task(task)
def delete_task(self, task_id: str, caller_id: str) -> None:
task = self.storage.get_task(task_id)
if not task:
raise InvalidTaskError(f"Task {task_id} does not exist")
# 权限验证
if caller_id != task.creator_id and not self.user_manager.is_admin(caller_id):
raise PermissionDeniedError("Only creator or admin can delete task")
self.storage.delete_task(task_id)
def archive_task(self, task_id: str, caller_id: str) -> None:
task = self.storage.get_task(task_id)
if not task:
raise InvalidTaskError(f"Task {task_id} does not exist")
# 权限验证
if caller_id != task.creator_id and not self.user_manager.is_admin(caller_id):
raise PermissionDeniedError("Only creator or admin can archive task")
# 验证状态
if task.status != TaskStatus.COMPLETED:
raise InvalidTaskError("Only completed tasks can be archived")
task.archived = True
task.updated_at = datetime.datetime.now()
self.storage.save_task(task)
def assign_task(self, task_id: str, caller_id: str, assignee_ids: List[str]) -> None:
task = self.storage.get_task(task_id)
if not task:
raise InvalidTaskError(f"Task {task_id} does not exist")
# 权限验证
if caller_id != task.creator_id and not self.user_manager.is_admin(caller_id):
raise PermissionDeniedError("Only creator or admin can assign task")
# 验证分配用户
for user_id in assignee_ids:
if not self.user_manager.is_user_valid(user_id):
raise InvalidUserError(f"User {user_id} does not exist")
task.assignees = list(set(assignee_ids)) # 去重
task.assignment_time = datetime.datetime.now()
task.updated_at = datetime.datetime.now()
self.storage.save_task(task)
def query_tasks(self, filters: Dict[str, Any]) -> List[Task]:
tasks = self.storage.get_all_tasks()
if "status" in filters:
try:
status = TaskStatus(filters["status"])
tasks = [t for t in tasks if t.status == status]
except ValueError:
raise InvalidTaskError(f"Invalid status: {filters['status']}")
if "priority" in filters:
try:
priority = TaskPriority(filters["priority"].strip().lower())
tasks = [t for t in tasks if t.priority == priority]
except ValueError:
raise InvalidTaskError(f"Invalid priority: {filters['priority']}")
if "assignee_id" in filters:
tasks = [t for t in tasks if filters["assignee_id"] in t.assignees]
if "creator_id" in filters:
tasks = [t for t in tasks if t.creator_id == filters["creator_id"]]
if "due_date_start" in filters:
tasks = [t for t in tasks if t.due_date >= filters["due_date_start"]]
if "due_date_end" in filters:
tasks = [t for t in tasks if t.due_date <= filters["due_date_end"]]
if "keyword" in filters:
tasks = [t for t in tasks if re.search(filters["keyword"], t.title + t.description, re.IGNORECASE)]
return tasks`
system_management.py
`import logging
import json
import os
from datetime import datetime
from typing import List, Dict, Optional
import sqlite3
from enum import Enum
Configure logging
logging.basicConfig(
filename='system.log',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
class NotificationType(Enum):
"""Enum for notification types."""
EMAIL = "email"
IN_APP = "in_app"
SMS = "sms"
class TaskPriority(Enum):
"""Enum for task priorities."""
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
class SystemConfig:
"""Manages system-wide configurations."""
def init(self):
self._config = {
"notification_type": NotificationType.IN_APP.value,
"default_priority": TaskPriority.MEDIUM.value,
"task_statuses": ["To Do", "In Progress", "Done"]
}
self._db_path = "task_management.db"
self._init_db()
def _init_db(self):
"""Initialize SQLite database for audit logs."""
try:
with sqlite3.connect(self._db_path) as conn:
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS audit_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task_id TEXT,
action TEXT,
user_id TEXT,
timestamp TEXT,
details TEXT
)
""")
conn.commit()
except sqlite3.Error as e:
logging.error(f"Database connection failed: {e}")
raise
def update_notification_type(self, notification_type: str) -> bool:
"""Update the default notification type."""
if notification_type not in [e.value for e in NotificationType]:
logging.warning(f"Invalid notification type: {notification_type}")
return False
self._config["notification_type"] = notification_type
logging.info(f"Notification type updated to {notification_type}")
return True
def update_default_priority(self, priority: str) -> bool:
"""Update the default task priority."""
if priority not in [e.value for e in TaskPriority]:
logging.warning(f"Invalid priority: {priority}")
return False
self._config["default_priority"] = priority
logging.info(f"Default priority updated to {priority}")
return True
def add_task_status(self, status: str) -> bool:
"""Add a new task status."""
if not status or not status.strip():
logging.warning("Task status cannot be empty")
return False
if status in self._config["task_statuses"]:
logging.warning(f"Task status '{status}' already exists")
return False
self._config["task_statuses"].append(status)
logging.info(f"New task status added: {status}")
return True
def get_config(self) -> Dict:
"""Get current system configuration."""
return self._config.copy()
class AuditLog:
"""Manages audit logs for task changes."""
def init(self, db_path: str):
self._db_path = db_path
def log_action(self, task_id: str, action: str, user_id: str, details: str = ""):
"""Log a task-related action."""
try:
with sqlite3.connect(self._db_path) as conn:
cursor = conn.cursor()
timestamp = datetime.now().isoformat()
cursor.execute(
"INSERT INTO audit_logs (task_id, action, user_id, timestamp, details) VALUES (?, ?, ?, ?, ?)",
(task_id, action, user_id, timestamp, details)
)
conn.commit()
logging.info(f"Logged action: {action} for task {task_id} by user {user_id}")
except sqlite3.Error as e:
logging.error(f"Failed to log action: {e}")
def get_task_history(self, task_id: str) -> List[Dict]:
"""Retrieve audit logs for a specific task."""
try:
with sqlite3.connect(self._db_path) as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM audit_logs WHERE task_id = ?", (task_id,))
rows = cursor.fetchall()
return [
{
"id": row[0],
"task_id": row[1],
"action": row[2],
"user_id": row[3],
"timestamp": row[4],
"details": row[5]
} for row in rows
]
except sqlite3.Error as e:
logging.error(f"Failed to retrieve task history: {e}")
return []
class DataBackup:
"""Handles data backup and restore."""
def init(self, db_path: str):
self._db_path = db_path
self._backup_dir = "backups"
def backup(self) -> Optional[str]:
"""Create a JSON backup of the audit logs."""
try:
os.makedirs(self._backup_dir, exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_file = f"{self._backup_dir}/backup_{timestamp}.json"
with sqlite3.connect(self._db_path) as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM audit_logs")
rows = cursor.fetchall()
data = [
{
"id": row[0],
"task_id": row[1],
"action": row[2],
"user_id": row[3],
"timestamp": row[4],
"details": row[5]
} for row in rows
]
with open(backup_file, 'w') as f:
json.dump(data, f, indent=2)
logging.info(f"Backup created: {backup_file}")
return backup_file
except (sqlite3.Error, OSError) as e:
logging.error(f"Backup failed: {e}")
return None
def restore(self, backup_file: str) -> bool:
"""Restore audit logs from a JSON backup."""
try:
with open(backup_file, 'r') as f:
data = json.load(f)
with sqlite3.connect(self._db_path) as conn:
cursor = conn.cursor()
cursor.execute("DELETE FROM audit_logs")
for entry in data:
cursor.execute(
"INSERT INTO audit_logs (id, task_id, action, user_id, timestamp, details) VALUES (?, ?, ?, ?, ?, ?)",
(entry["id"], entry["task_id"], entry["action"], entry["user_id"], entry["timestamp"], entry["details"])
)
conn.commit()
logging.info(f"Restored from backup: {backup_file}")
return True
except (sqlite3.Error, OSError, json.JSONDecodeError) as e:
logging.error(f"Restore failed: {e}")
return False
Example usage
if name == "main":
# Initialize system components
config = SystemConfig()
audit = AuditLog("task_management.db")
backup = DataBackup("task_management.db")
# Configure notification type
config.update_notification_type(NotificationType.IN_APP.value)
# Add a new task status
config.add_task_status("Pending Review")
# Log some actions
audit.log_action("TASK001", "created", "admin1", "Task created with title 'Initial Task'")
audit.log_action("TASK001", "assigned", "admin1", "Assigned to user2")
audit.log_action("TASK001", "completed", "user2", "Task marked as done")
# View task history
history = audit.get_task_history("TASK001")
for entry in history:
print(f"Task {entry['task_id']} - {entry['action']} by {entry['user_id']} at {entry['timestamp']}: {entry['details']}")
# Create a backup
backup_file = backup.backup()
if backup_file:
print(f"Backup created at {backup_file}")
# Restore from backup (example)
if backup_file:
backup.restore(backup_file)`
session_manager.py
`import uuid
from datetime import datetime, timedelta
from typing import Optional
from user import User, UserNotFoundError
class Session:
"""会话数据模型"""
def init(self, session_token: str, user_id: str, expires_at: datetime):
self.session_token = session_token
self.user_id = user_id
self.expires_at = expires_at
class SessionManager:
"""会话管理类,负责令牌生成、验证和清除"""
# 会话超时时间(30分钟)
SESSION_TIMEOUT = timedelta(minutes=30)
def __init__(self):
# 内存存储会话数据
self._sessions = {}
def create_session(self, user: User) -> str:
"""为用户创建会话"""
session_token = str(uuid.uuid4())
expires_at = datetime.now() + self.SESSION_TIMEOUT
session = Session(
session_token=session_token,
user_id=user.user_id,
expires_at=expires_at
)
self._sessions[session_token] = session
return session_token
def validate_session(self, session_token: str) -> Optional[User]:
"""验证会话令牌"""
if session_token not in self._sessions:
return None
session = self._sessions[session_token]
if datetime.now() > session.expires_at:
del self._sessions[session_token]
return None
return session
def clear_session(self, session_token: str) -> None:
"""清除会话"""
if session_token in self._sessions:
del self._sessions[session_token]`
notification_service.py
`import asyncio
from dataclasses import dataclass
from datetime import datetime, timedelta
from enum import Enum
from typing import List, Optional, Dict
import smtplib
from email.mime.text import MIMEText
通知类型
class NotificationType(Enum):
TASK_ASSIGNED = "task_assigned"
STATUS_CHANGED = "status_changed"
DEADLINE_APPROACHING = "deadline_approaching"
通知优先级
class Priority(Enum):
HIGH = "high" # 紧急,实时发送
LOW = "low" # 普通,可延迟
通知状态
class NotificationStatus(Enum):
SUCCESS = "success"
FAILED = "failed"
@dataclass
class Notification:
notification_id: int
task_id: int
user_id: int
notification_type: NotificationType
priority: Priority
content: str
send_time: datetime
status: NotificationStatus
failure_reason: Optional[str] = None
@dataclass
class NotificationSettings:
user_id: int
receive_task_assigned: bool = True
receive_status_changed: bool = True
receive_deadline_approaching: bool = True
@dataclass
class TaskDetails:
task_id: int
title: str
assignees: List[int]
creator_id: int
deadline: Optional[datetime] = None
@dataclass
class UserContact:
user_id: int
email: str
class NotificationService:
def init(self, task_manager, user_manager, smtp_config: Dict[str, str] = None):
"""初始化通知服务,注入任务管理和用户管理依赖"""
self.task_manager = task_manager # 任务管理模块接口
self.user_manager = user_manager # 用户管理模块接口
self.smtp_config = smtp_config or {} # SMTP 配置(主机、端口、用户、密码)
self.notifications: List[Notification] = [] # 通知历史(内存存储)
self.notification_counter = 0 # 通知ID计数器
self.email_queue = asyncio.Queue() # 异步邮件队列
self._start_email_queue()
def _start_email_queue(self):
"""启动异步邮件发送队列"""
async def process_email_queue():
while True:
email_task = await self.email_queue.get()
await self._send_email_async(*email_task)
self.email_queue.task_done()
asyncio.create_task(process_email_queue())
def _generate_notification_id(self) -> int:
"""生成唯一通知ID"""
self.notification_counter += 1
return self.notification_counter
def _create_notification_content(self, task: TaskDetails, notification_type: NotificationType) -> str:
"""生成通知内容"""
if notification_type == NotificationType.TASK_ASSIGNED:
return f"任务 '{task.title}' 已分配给您"
elif notification_type == NotificationType.STATUS_CHANGED:
return f"任务 '{task.title}' 状态已变更"
elif notification_type == NotificationType.DEADLINE_APPROACHING:
return f"任务 '{task.title}' 将于 {task.deadline} 到期"
return ""
def _send_site_message(self, user_id: int, content: str) -> bool:
"""发送站内消息(模拟实现)"""
try:
# 假设调用站内消息系统接口
print(f"发送站内消息给用户 {user_id}: {content}")
return True
except Exception as e:
print(f"站内消息发送失败: {e}")
return False
async def _send_email_async(self, email: str, content: str, subject: str) -> tuple[bool, Optional[str]]:
"""异步发送邮件"""
try:
msg = MIMEText(content)
msg['Subject'] = subject
msg['From'] = self.smtp_config.get('from_email', 'no-reply@taskmanager.com')
msg['To'] = email
with smtplib.SMTP(self.smtp_config.get('host', 'localhost'), self.smtp_config.get('port', 587)) as server:
if self.smtp_config.get('user') and self.smtp_config.get('password'):
server.login(self.smtp_config['user'], self.smtp_config['password'])
server.send_message(msg)
return True, None
except Exception as e:
return False, str(e)
def _send_notification(self, user_id: int, task_id: int, notification_type: NotificationType, priority: Priority) -> Notification:
"""生成并发送通知"""
task = self.task_manager.get_task_details(task_id)
settings = self.user_manager.get_notification_settings(user_id)
# 检查用户通知偏好
if (
(notification_type == NotificationType.TASK_ASSIGNED and not settings.receive_task_assigned) or
(notification_type == NotificationType.STATUS_CHANGED and not settings.receive_status_changed) or
(notification_type == NotificationType.DEADLINE_APPROACHING and not settings.receive_deadline_approaching)
):
return Notification(
notification_id=self._generate_notification_id(),
task_id=task_id,
user_id=user_id,
notification_type=notification_type,
priority=priority,
content="",
send_time=datetime.now(),
status=NotificationStatus.FAILED,
failure_reason="用户已关闭此类型通知"
)
content = self._create_notification_content(task, notification_type)
notification = Notification(
notification_id=self._generate_notification_id(),
task_id=task_id,
user_id=user_id,
notification_type=notification_type,
priority=priority,
content=content,
send_time=datetime.now(),
status=NotificationStatus.SUCCESS
)
# 发送站内消息
if not self._send_site_message(user_id, content):
notification.status = NotificationStatus.FAILED
notification.failure_reason = "站内消息发送失败"
# 发送邮件
contact = self.user_manager.get_user_contact(user_id)
if contact.email:
subject = f"任务通知: {task.title}"
if priority == Priority.HIGH:
# 紧急通知同步发送
success, reason = asyncio.run(self._send_email_async(contact.email, content, subject))
if not success:
notification.status = NotificationStatus.FAILED
notification.failure_reason = f"邮件发送失败: {reason}"
else:
# 普通通知加入异步队列
asyncio.create_task(self.email_queue.put((contact.email, content, subject)))
self.notifications.append(notification)
return notification
def handle_task_assigned(self, task_id: int, user_ids: List[int]) -> None:
"""处理任务分配事件"""
for user_id in user_ids:
self._send_notification(user_id, task_id, NotificationType.TASK_ASSIGNED, Priority.HIGH)
def handle_status_changed(self, task_id: int, new_status: str) -> None:
"""处理任务状态变更事件"""
task = self.task_manager.get_task_details(task_id)
for user_id in task.assignees + [task.creator_id]:
self._send_notification(user_id, task_id, NotificationType.STATUS_CHANGED, Priority.LOW)
def handle_deadline_approaching(self, task_id: int, deadline: datetime) -> None:
"""处理截止日期临近事件"""
task = self.task_manager.get_task_details(task_id)
for user_id in task.assignees + [task.creator_id]:
self._send_notification(user_id, task_id, NotificationType.DEADLINE_APPROACHING, Priority.LOW)
def get_notification_history(self, user_id: int, days: int = 7) -> List[Notification]:
"""查询用户最近7天的通知记录"""
cutoff_time = datetime.now() - timedelta(days=days)
return [
notification for notification in self.notifications
if notification.user_id == user_id and notification.send_time >= cutoff_time
]
def update_notification_settings(self, user_id: int, settings: NotificationSettings) -> None:
"""更新用户通知偏好"""
self.user_manager.update_notification_settings(user_id, settings)
def get_notification_settings(self, user_id: int) -> NotificationSettings:
"""获取用户通知偏好"""
return self.user_manager.get_notification_settings(user_id)`
浙公网安备 33010602011771号