KarlWitte

导航

软件工程结对作业系统重构后完整代码

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)`

posted on 2025-05-23 22:26  许九卿  阅读(21)  评论(0)    收藏  举报