PHP项目用户数据导出与删除

wen PHP项目 4

本文目录导读:

PHP项目用户数据导出与删除

  1. 用户数据导出功能
  2. 用户数据删除功能
  3. 定时清理任务
  4. 数据库表结构
  5. 安全建议

我来帮你梳理PHP项目中用户数据导出与删除的完整实现方案。

用户数据导出功能

1 导出为CSV格式

<?php
// export_user_data.php
require_once 'config/database.php';
require_once 'classes/UserDataExport.php';
class UserDataExport {
    private $db;
    private $userId;
    public function __construct($db, $userId) {
        $this->db = $db;
        $this->userId = $userId;
    }
    /**
     * 导出用户所有数据
     */
    public function exportAllData() {
        $data = [
            'profile' => $this->getProfile(),
            'orders' => $this->getOrders(),
            'messages' => $this->getMessages(),
            'activity_logs' => $this->getActivityLogs(),
            'comments' => $this->getComments()
        ];
        return $data;
    }
    /**
     * 导出为CSV文件
     */
    public function exportToCSV() {
        $data = $this->exportAllData();
        // 设置headers
        header('Content-Type: text/csv; charset=utf-8');
        header('Content-Disposition: attachment; filename="user_data_' . date('Y-m-d_H-i-s') . '.csv"');
        // 创建输出流
        $output = fopen('php://output', 'w');
        // 添加BOM for UTF-8
        fprintf($output, chr(0xEF).chr(0xBB).chr(0xBF));
        // 导出个人信息
        fputcsv($output, ['=== 个人信息 ===']);
        fputcsv($output, ['字段', '值']);
        foreach ($data['profile'] as $key => $value) {
            fputcsv($output, [$key, $value]);
        }
        // 导出订单数据
        fputcsv($output, []);
        fputcsv($output, ['=== 订单记录 ===']);
        fputcsv($output, ['订单ID', '金额', '状态', '创建时间']);
        foreach ($data['orders'] as $order) {
            fputcsv($output, [
                $order['id'],
                $order['amount'],
                $order['status'],
                $order['created_at']
            ]);
        }
        fclose($output);
        exit;
    }
    /**
     * 导出为JSON格式
     */
    public function exportToJSON() {
        $data = $this->exportAllData();
        header('Content-Type: application/json; charset=utf-8');
        header('Content-Disposition: attachment; filename="user_data_' . date('Y-m-d_H-i-s') . '.json"');
        echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
        exit;
    }
    /**
     * 导出为ZIP压缩包
     */
    public function exportToZIP() {
        $data = $this->exportAllData();
        $tempDir = sys_get_temp_dir() . '/export_' . $this->userId . '_' . time();
        // 创建临时目录
        if (!is_dir($tempDir)) {
            mkdir($tempDir, 0777, true);
        }
        // 写入各数据文件
        file_put_contents($tempDir . '/profile.json', json_encode($data['profile'], JSON_UNESCAPED_UNICODE));
        file_put_contents($tempDir . '/orders.json', json_encode($data['orders'], JSON_UNESCAPED_UNICODE));
        file_put_contents($tempDir . '/messages.json', json_encode($data['messages'], JSON_UNESCAPED_UNICODE));
        // 创建ZIP文件
        $zipFile = sys_get_temp_dir() . '/user_data_' . $this->userId . '.zip';
        $zip = new ZipArchive();
        if ($zip->open($zipFile, ZipArchive::CREATE) === TRUE) {
            $files = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($tempDir),
                RecursiveIteratorIterator::LEAVES_ONLY
            );
            foreach ($files as $file) {
                if (!$file->isDir()) {
                    $filePath = $file->getRealPath();
                    $relativePath = substr($filePath, strlen($tempDir) + 1);
                    $zip->addFile($filePath, $relativePath);
                }
            }
            $zip->close();
            // 下载ZIP文件
            header('Content-Type: application/zip');
            header('Content-Disposition: attachment; filename="user_data_export.zip"');
            header('Content-Length: ' . filesize($zipFile));
            readfile($zipFile);
            // 清理临时文件
            array_map('unlink', glob("$tempDir/*.*"));
            rmdir($tempDir);
            unlink($zipFile);
        }
        exit;
    }
    private function getProfile() {
        $stmt = $this->db->prepare("SELECT username, email, phone, created_at, last_login FROM users WHERE id = ?");
        $stmt->execute([$this->userId]);
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }
    private function getOrders() {
        $stmt = $this->db->prepare("SELECT id, amount, status, created_at FROM orders WHERE user_id = ?");
        $stmt->execute([$this->userId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    private function getMessages() {
        $stmt = $this->db->prepare("SELECT * FROM messages WHERE user_id = ?");
        $stmt->execute([$this->userId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    private function getActivityLogs() {
        $stmt = $this->db->prepare("SELECT * FROM activity_logs WHERE user_id = ? ORDER BY created_at DESC LIMIT 100");
        $stmt->execute([$this->userId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    private function getComments() {
        $stmt = $this->db->prepare("SELECT * FROM comments WHERE user_id = ?");
        $stmt->execute([$this->userId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
}

2 导出页面界面

// export_page.php
<!DOCTYPE html>
<html>
<head>数据导出</title>
    <style>
        .export-container {
            max-width: 600px;
            margin: 50px auto;
            padding: 30px;
            background: #fff;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        .export-options {
            margin: 20px 0;
        }
        .export-btn {
            display: inline-block;
            padding: 10px 20px;
            margin: 5px;
            background: #007bff;
            color: white;
            text-decoration: none;
            border-radius: 4px;
            border: none;
            cursor: pointer;
        }
        .export-btn:hover {
            background: #0056b3;
        }
        .warning {
            color: #856404;
            background-color: #fff3cd;
            border: 1px solid #ffeeba;
            padding: 15px;
            border-radius: 4px;
            margin: 20px 0;
        }
    </style>
</head>
<body>
    <div class="export-container">
        <h2>导出我的数据</h2>
        <div class="warning">
            <strong>注意:</strong>导出的数据包含您的个人信息,请妥善保管。
        </div>
        <div class="export-options">
            <h3>选择导出格式:</h3>
            <a href="export.php?format=csv" class="export-btn">导出CSV</a>
            <a href="export.php?format=json" class="export-btn">导出JSON</a>
            <a href="export.php?format=zip" class="export-btn">导出ZIP压缩包</a>
        </div>
        <form method="POST" action="export.php">
            <h3>选择性导出:</h3>
            <label><input type="checkbox" name="data_types[]" value="profile" checked> 个人信息</label><br>
            <label><input type="checkbox" name="data_types[]" value="orders" checked> 订单记录</label><br>
            <label><input type="checkbox" name="data_types[]" value="messages" checked> 消息记录</label><br>
            <label><input type="checkbox" name="data_types[]" value="comments" checked> 评论记录</label><br>
            <button type="submit" name="export_custom" class="export-btn" style="margin-top: 15px;">导出选中数据</button>
        </form>
    </div>
</body>
</html>

用户数据删除功能

1 完整删除实现

<?php
// delete_user_data.php
require_once 'config/database.php';
require_once 'classes/UserDataDeletion.php';
class UserDataDeletion {
    private $db;
    private $userId;
    private $logFile;
    public function __construct($db, $userId) {
        $this->db = $db;
        $this->userId = $userId;
        $this->logFile = 'deletion_logs.txt';
    }
    /**
     * 软删除用户(标记删除)
     */
    public function softDelete() {
        try {
            $this->db->beginTransaction();
            // 标记用户为已删除
            $stmt = $this->db->prepare("UPDATE users SET 
                status = 'deleted',
                deleted_at = NOW(),
                username = CONCAT('deleted_', id),
                email = CONCAT('deleted_', id, '@removed.com')
                WHERE id = ?");
            $stmt->execute([$this->userId]);
            // 匿名化相关数据
            $this->anonymizeRelatedData();
            $this->db->commit();
            $this->logAction('soft_delete', 'User data anonymized and marked as deleted');
            return true;
        } catch (Exception $e) {
            $this->db->rollBack();
            $this->logAction('soft_delete_failed', $e->getMessage());
            throw $e;
        }
    }
    /**
     * 硬删除用户(彻底删除)
     */
    public function hardDelete() {
        try {
            $this->db->beginTransaction();
            // 删除用户的所有数据
            $tables = [
                'user_sessions',
                'password_resets',
                'order_items',
                'orders',
                'comments',
                'messages',
                'activity_logs',
                'user_meta',
                'users'
            ];
            foreach ($tables as $table) {
                $this->deleteFromTable($table);
            }
            $this->db->commit();
            $this->logAction('hard_delete', 'All user data permanently deleted');
            return true;
        } catch (Exception $e) {
            $this->db->rollBack();
            $this->logAction('hard_delete_failed', $e->getMessage());
            throw $e;
        }
    }
    /**
     * 选择性删除特定数据
     */
    public function selectiveDelete($dataTypes) {
        try {
            $this->db->beginTransaction();
            $allowedTypes = ['orders', 'messages', 'comments', 'activity_logs'];
            foreach ($dataTypes as $type) {
                if (in_array($type, $allowedTypes)) {
                    $this->deleteFromTable($type);
                }
            }
            $this->db->commit();
            $this->logAction('selective_delete', 'Selected data types deleted: ' . implode(', ', $dataTypes));
            return true;
        } catch (Exception $e) {
            $this->db->rollBack();
            $this->logAction('selective_delete_failed', $e->getMessage());
            throw $e;
        }
    }
    /**
     * 删除前备份用户数据
     */
    public function backupBeforeDelete() {
        $exporter = new UserDataExport($this->db, $this->userId);
        $data = $exporter->exportAllData();
        $backupFile = 'backups/user_' . $this->userId . '_' . date('Y-m-d_H-i-s') . '.json';
        if (!is_dir('backups')) {
            mkdir('backups', 0777, true);
        }
        file_put_contents($backupFile, json_encode($data, JSON_PRETTY_PRINT));
        return $backupFile;
    }
    /**
     * 带确认的删除流程
     */
    public function deleteWithConfirmation($confirmationToken) {
        // 验证确认令牌
        if (!$this->validateConfirmationToken($confirmationToken)) {
            throw new Exception('Invalid confirmation token');
        }
        // 备份数据
        $backupFile = $this->backupBeforeDelete();
        // 执行硬删除
        $result = $this->hardDelete();
        // 发送确认邮件
        $this->sendDeletionConfirmationEmail($backupFile);
        return $result;
    }
    private function deleteFromTable($table) {
        $allowedTables = [
            'user_sessions', 'password_resets', 'order_items', 
            'orders', 'comments', 'messages', 'activity_logs', 
            'user_meta', 'users'
        ];
        if (!in_array($table, $allowedTables)) {
            throw new Exception("Unauthorized table: $table");
        }
        $columnMap = [
            'users' => 'id',
            'user_sessions' => 'user_id',
            'password_resets' => 'user_id',
            'order_items' => 'user_id',
            'orders' => 'user_id',
            'comments' => 'user_id',
            'messages' => 'user_id',
            'activity_logs' => 'user_id',
            'user_meta' => 'user_id'
        ];
        $column = $columnMap[$table] ?? 'user_id';
        $stmt = $this->db->prepare("DELETE FROM $table WHERE $column = ?");
        $stmt->execute([$this->userId]);
        return $stmt->rowCount();
    }
    private function anonymizeRelatedData() {
        // 匿名化订单数据
        $stmt = $this->db->prepare("UPDATE orders SET 
            shipping_address = 'REMOVED',
            billing_address = 'REMOVED',
            phone = 'REMOVED'
            WHERE user_id = ?");
        $stmt->execute([$this->userId]);
        // 匿名化评论
        $stmt = $this->db->prepare("UPDATE comments SET 
            content = '[This comment has been removed]'
            WHERE user_id = ?");
        $stmt->execute([$this->userId]);
        // 删除会话
        $stmt = $this->db->prepare("DELETE FROM user_sessions WHERE user_id = ?");
        $stmt->execute([$this->userId]);
    }
    private function validateConfirmationToken($token) {
        // 从数据库验证token
        $stmt = $this->db->prepare("SELECT * FROM deletion_tokens 
            WHERE user_id = ? AND token = ? AND expires_at > NOW()");
        $stmt->execute([$this->userId, $token]);
        return $stmt->fetch() !== false;
    }
    private function sendDeletionConfirmationEmail($backupFile) {
        $userEmail = $this->getUserEmail();
        $subject = "Account Deletion Confirmation";
        $message = "Your account has been successfully deleted. 
                    If you need to recover your data, please contact support within 30 days.
                    Backup file: $backupFile";
        // 邮件发送逻辑
        mail($userEmail, $subject, $message);
    }
    private function getUserEmail() {
        // 缓存用户邮箱用于最后通知
        static $email = null;
        if ($email === null) {
            $stmt = $this->db->prepare("SELECT email FROM users WHERE id = ?");
            $stmt->execute([$this->userId]);
            $result = $stmt->fetch();
            $email = $result ? $result['email'] : '';
        }
        return $email;
    }
    private function logAction($action, $details) {
        $logEntry = date('Y-m-d H:i:s') . " - User ID: {$this->userId} - Action: $action - Details: $details\n";
        file_put_contents($this->logFile, $logEntry, FILE_APPEND);
    }
}
// 处理删除请求
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    session_start();
    if (!isset($_SESSION['user_id'])) {
        die('Unauthorized access');
    }
    $userId = $_SESSION['user_id'];
    $deletion = new UserDataDeletion($db, $userId);
    try {
        if (isset($_POST['confirm_delete'])) {
            // 发送确认邮件
            $token = bin2hex(random_bytes(32));
            // 保存token到数据库
            // 发送确认邮件
            $confirmLink = "http://example.com/confirm_delete.php?token=$token";
            mail($userEmail, "Confirm Account Deletion", "Click to confirm: $confirmLink");
            echo "Confirmation email sent. Please check your email to complete the deletion.";
        } elseif (isset($_POST['soft_delete'])) {
            $deletion->softDelete();
            echo "Account has been deactivated. You can restore it within 30 days.";
        } elseif (isset($_POST['hard_delete'])) {
            $deletion->hardDelete();
            echo "Account permanently deleted.";
            session_destroy();
        }
    } catch (Exception $e) {
        echo "Error: " . $e->getMessage();
    }
}

2 删除确认页面

// delete_account.php
<!DOCTYPE html>
<html>
<head>删除账户</title>
    <style>
        .delete-container {
            max-width: 500px;
            margin: 50px auto;
            padding: 30px;
            background: #fff;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        .danger-zone {
            border: 2px solid #dc3545;
            border-radius: 8px;
            padding: 20px;
            margin: 20px 0;
        }
        .confirm-input {
            width: 100%;
            padding: 10px;
            margin: 10px 0;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
        .btn-danger {
            background: #dc3545;
            color: white;
            padding: 10px 20px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .btn-danger:hover {
            background: #c82333;
        }
        .options {
            margin: 15px 0;
        }
        .option-item {
            margin: 10px 0;
            padding: 10px;
            background: #f8f9fa;
            border-radius: 4px;
        }
    </style>
</head>
<body>
    <div class="delete-container">
        <h2>账户删除选项</h2>
        <div class="danger-zone">
            <h3>⚠️ 危险操作</h3>
            <p>删除账户后,您将失去所有数据,此操作可能不可逆。</p>
        </div>
        <div class="options">
            <div class="option-item">
                <h4>选项1: 软删除(推荐)</h4>
                <p>停用账户,数据被匿名化,您可以在30天内恢复。</p>
                <form method="POST">
                    <button type="submit" name="soft_delete" class="btn btn-warning">
                        软删除账户
                    </button>
                </form>
            </div>
            <div class="option-item">
                <h4>选项2: 彻底删除</h4>
                <p>永久删除所有数据,无法恢复。</p>
                <form method="POST" onsubmit="return confirmDeletion()">
                    <label>请键入 "DELETE" 确认操作:</label>
                    <input type="text" class="confirm-input" id="confirm_text" 
                           placeholder="输入 DELETE" required>
                    <button type="submit" name="hard_delete" class="btn-danger" 
                            id="delete_btn" disabled>
                        彻底删除账户
                    </button>
                </form>
            </div>
            <div class="option-item">
                <h4>选项3: 选择性删除</h4>
                <p>选择要删除的数据类型。</p>
                <form method="POST">
                    <label><input type="checkbox" name="data_types[]" value="orders"> 删除订单记录</label><br>
                    <label><input type="checkbox" name="data_types[]" value="messages"> 删除消息记录</label><br>
                    <label><input type="checkbox" name="data_types[]" value="comments"> 删除评论</label><br>
                    <button type="submit" name="selective_delete" class="btn btn-secondary">
                        删除选定数据
                    </button>
                </form>
            </div>
        </div>
        <div class="data-export">
            <h4>在删除前,建议您先导出数据</h4>
            <a href="export.php" class="export-btn">导出我的数据</a>
        </div>
    </div>
    <script>
    function confirmDeletion() {
        const confirmText = document.getElementById('confirm_text').value;
        if (confirmText !== 'DELETE') {
            alert('请正确输入 DELETE 确认删除');
            return false;
        }
        return confirm('您确定要永久删除账户吗?此操作不可恢复!');
    }
    // 启用删除按钮当输入正确时
    document.getElementById('confirm_text').addEventListener('input', function() {
        document.getElementById('delete_btn').disabled = this.value !== 'DELETE';
    });
    </script>
</body>
</html>

定时清理任务

<?php
// cron/cleanup_deleted_users.php
/**
 * 定时清理过期的软删除用户
 * 建议运行:0 3 * * * php /path/to/cron/cleanup_deleted_users.php
 */
require_once '../config/database.php';
class DeletedUserCleanup {
    private $db;
    public function __construct($db) {
        $this->db = $db;
    }
    public function cleanExpiredUsers() {
        try {
            $this->db->beginTransaction();
            // 查找超过30天的软删除用户
            $stmt = $this->db->prepare("
                SELECT id, email, deleted_at 
                FROM users 
                WHERE status = 'deleted' 
                AND deleted_at <= DATE_SUB(NOW(), INTERVAL 30 DAY)
            ");
            $stmt->execute();
            $expiredUsers = $stmt->fetchAll(PDO::FETCH_ASSOC);
            foreach ($expiredUsers as $user) {
                $deletion = new UserDataDeletion($this->db, $user['id']);
                $deletion->hardDelete();
                echo "Permanently deleted user ID: {$user['id']}\n";
            }
            // 清理备份文件(超过90天)
            $this->cleanOldBackups();
            $this->db->commit();
            echo "Cleanup completed. " . count($expiredUsers) . " users permanently deleted.\n";
        } catch (Exception $e) {
            $this->db->rollBack();
            echo "Error during cleanup: " . $e->getMessage() . "\n";
        }
    }
    private function cleanOldBackups() {
        $backupDir = '../backups/';
        $files = glob($backupDir . '*.json');
        foreach ($files as $file) {
            if (filemtime($file) < strtotime('-90 days')) {
                unlink($file);
                echo "Deleted old backup: $file\n";
            }
        }
    }
}
// 执行清理
$cleanup = new DeletedUserCleanup($db);
$cleanup->cleanExpiredUsers();

数据库表结构

-- 用户表添加软删除字段
ALTER TABLE users ADD COLUMN status VARCHAR(20) DEFAULT 'active';
ALTER TABLE users ADD COLUMN deleted_at TIMESTAMP NULL;
-- 删除令牌表
CREATE TABLE IF NOT EXISTS deletion_tokens (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    token VARCHAR(64) NOT NULL,
    expires_at TIMESTAMP NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_user_token (user_id, token)
);
-- 删除日志表
CREATE TABLE IF NOT EXISTS deletion_logs (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    action_type VARCHAR(50) NOT NULL,
    details TEXT,
    ip_address VARCHAR(45),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

安全建议

  1. 权限验证:确保只有用户本人或管理员可以操作
  2. 数据备份:删除前强制备份
  3. 多步确认:重要操作需要多重确认
  4. 日志记录:记录所有删除操作
  5. 冷却期:设置删除确认冷却期
  6. 速率限制:防止滥用删除功能

这个实现提供了完整的用户数据导出和删除功能,包括多种导出格式、软删除和硬删除选项,以及安全措施和定时清理任务。

抱歉,评论功能暂时关闭!