本文目录导读:

我来帮你梳理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
);
安全建议
- 权限验证:确保只有用户本人或管理员可以操作
- 数据备份:删除前强制备份
- 多步确认:重要操作需要多重确认
- 日志记录:记录所有删除操作
- 冷却期:设置删除确认冷却期
- 速率限制:防止滥用删除功能
这个实现提供了完整的用户数据导出和删除功能,包括多种导出格式、软删除和硬删除选项,以及安全措施和定时清理任务。