Files
leonapp/admin/manage_versions.php
Leonmmcoset e2d0e10cfe feat(版本管理): 添加Markdown预览功能并改进版本删除逻辑
- 在版本上传和编辑表单中添加Markdown实时预览功能
- 使用marked.js库实现Markdown解析
- 改进版本删除逻辑,使用事务处理确保数据一致性
- 为历史版本日志添加Markdown渲染切换功能
2025-09-23 18:08:20 +08:00

424 lines
19 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
require_once '../config.php';
session_start();
// 检查是否已登录
if (!isset($_SESSION['admin'])) {
header('Location: login.php');
exit();
}
// 非全部权限管理员重定向到对应权限页面
if ($_SESSION['admin']['permission'] != 'all') {
$redirect = $_SESSION['admin']['permission'] == 'say' ? 'announcements.php' : 'review_apps.php';
header("Location: $redirect");
exit();
}
// 验证App ID
if (!isset($_GET['app_id']) || !is_numeric($_GET['app_id'])) {
header('Location: index.php?error=无效的App ID');
exit;
}
$appId = $_GET['app_id'];
// 获取App信息
$app = null;
$getAppSql = "SELECT * FROM apps WHERE id = ?";
$stmt = $conn->prepare($getAppSql);
$stmt->bind_param("i", $appId);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows === 0) {
header('Location: index.php?error=App不存在');
exit;
}
$app = $result->fetch_assoc();
// 获取所有版本
$versions = [];
$getVersionsSql = "SELECT * FROM app_versions WHERE app_id = ? ORDER BY created_at DESC";
$stmt = $conn->prepare($getVersionsSql);
$stmt->bind_param("i", $appId);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
$versions[] = $row;
}
$success = '';
$error = '';
// 处理添加版本
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_version'])) {
$version = $_POST['version'];
$changelog = $_POST['changelog'];
if (empty($version)) {
$error = '版本号不能为空';
} elseif (empty($_FILES['app_file']['name'])) {
$error = '请上传App文件';
} else {
$uploadDir = '../files/';
$fileName = basename($_FILES['app_file']['name']);
$targetPath = $uploadDir . $fileName;
if (move_uploaded_file($_FILES['app_file']['tmp_name'], $targetPath)) {
$insertVersionSql = "INSERT INTO app_versions (app_id, version, changelog, file_path, created_at) VALUES (?, ?, ?, ?, NOW())";
$stmt = $conn->prepare($insertVersionSql);
$stmt->bind_param("isss", $appId, $version, $changelog, $targetPath);
if ($stmt->execute() === TRUE) {
header('Location: manage_versions.php?app_id=' . $appId . '&success=版本添加成功');
exit;
} else {
$error = '版本添加失败: ' . $conn->error;
unlink($targetPath); // 删除已上传的文件
}
} else {
$error = '文件上传失败';
}
}
}
// 处理版本删除请求
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_version'])) {
// 设置内容类型为纯文本
header('Content-Type: text/plain');
$versionId = $_POST['version_id'];
$appId = $_POST['app_id'];
// 初始化消息变量
$message = '';
// 开始事务
$conn->begin_transaction();
try {
// 1. 获取文件路径
$filePath = '';
$getFilePathSql = "SELECT file_path FROM app_versions WHERE id = ? AND app_id = ?";
$getFileStmt = $conn->prepare($getFilePathSql);
if (!$getFileStmt) {
throw new Exception("获取文件路径查询准备失败: " . $conn->error);
}
$getFileStmt->bind_param("ii", $versionId, $appId);
$getFileStmt->execute();
$fileResult = $getFileStmt->get_result();
if ($fileResult->num_rows > 0) {
$fileRow = $fileResult->fetch_assoc();
$filePath = $fileRow['file_path'];
}
// 2. 从数据库删除版本记录
$deleteVersionSql = "DELETE FROM app_versions WHERE id = ? AND app_id = ?";
$deleteVersionStmt = $conn->prepare($deleteVersionSql);
if (!$deleteVersionStmt) {
throw new Exception("版本删除查询准备失败: " . $conn->error);
}
$deleteVersionStmt->bind_param("ii", $versionId, $appId);
if (!$deleteVersionStmt->execute()) {
throw new Exception("版本删除执行失败: " . $conn->error);
}
// 检查是否有记录被删除
if ($deleteVersionStmt->affected_rows === 0) {
throw new Exception("未找到要删除的版本记录");
}
// 3. 如果数据库删除成功,尝试删除文件(即使文件删除失败也不回滚数据库操作)
if (!empty($filePath) && file_exists($filePath)) {
if (!unlink($filePath)) {
// 文件删除失败不影响数据库操作,继续处理
}
}
// 提交事务
$conn->commit();
$message = '版本删除成功';
} catch (Exception $e) {
// 回滚事务
$conn->rollback();
$message = '版本删除失败: ' . $e->getMessage();
}
// 只输出消息不包含任何HTML
echo $message;
// 确保脚本终止,不执行后续代码
exit;
}
// 处理编辑版本
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['edit_version'])) {
$versionId = $_POST['version_id'];
$version = $_POST['version'];
$changelog = $_POST['changelog'];
if (empty($version)) {
$error = '版本号不能为空';
} else {
// 检查是否上传了新文件
$fileUpdate = '';
$params = ['ss', $version, $changelog, $versionId, $appId];
if (!empty($_FILES['new_app_file']['name'])) {
$uploadDir = '../files/';
$fileName = basename($_FILES['new_app_file']['name']);
$targetPath = $uploadDir . $fileName;
if (move_uploaded_file($_FILES['new_app_file']['tmp_name'], $targetPath)) {
// 获取旧文件路径
$getOldFileSql = "SELECT file_path FROM app_versions WHERE id = ? AND app_id = ?";
$stmt = $conn->prepare($getOldFileSql);
$stmt->bind_param("ii", $versionId, $appId);
$stmt->execute();
$result = $stmt->get_result();
$oldVersion = $result->fetch_assoc();
// 删除旧文件
if (file_exists($oldVersion['file_path'])) {
unlink($oldVersion['file_path']);
}
$fileUpdate = ", file_path = ?";
$params[0] = 'sss';
$params[] = $targetPath;
} else {
$error = '文件上传失败';
}
}
if (empty($error)) {
$updateVersionSql = "UPDATE app_versions SET version = ?, changelog = ?" . $fileUpdate . " WHERE id = ? AND app_id = ?";
$stmt = $conn->prepare($updateVersionSql);
// 动态绑定参数
$stmt->bind_param(...$params);
if ($stmt->execute() === TRUE) {
header('Location: manage_versions.php?app_id=' . $appId . '&success=版本更新成功');
exit;
} else {
$error = '版本更新失败: ' . $conn->error;
}
}
}
}
// 获取URL参数中的成功/错误消息
if (isset($_GET['success'])) {
$success = $_GET['success'];
} elseif (isset($_GET['error'])) {
$error = $_GET['error'];
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>管理版本 - <?php echo htmlspecialchars($app['name']); ?></title>
<!-- Bootstrap CSS -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<!-- 自定义CSS -->
<link rel="stylesheet" href="../styles.css">
<style>
.version-card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.version-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
}
.action-btn {
margin: 0 2px;
}
.modal-backdrop {
backdrop-filter: blur(5px);
}
</style>
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<a class="navbar-brand" href="index.php"><?php echo APP_STORE_NAME; ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="index.php">App列表</a>
</li>
<li class="nav-item">
<a class="nav-link" href="editapp.php?id=<?php echo $appId; ?>">返回编辑App</a>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="manage_versions.php?app_id=<?php echo $appId; ?>">管理版本</a>
</li>
<li class="nav-item">
<a class="nav-link" href="?logout=true">退出登录</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
<div class="row mb-4">
<div class="col">
<h1><i class="fas fa-code-branch me-2"></i>管理版本: <?php echo htmlspecialchars($app['name']); ?></h1>
<p class="text-muted">管理该应用的所有版本</p>
</div>
<div class="col text-end">
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addVersionModal">
<i class="fas fa-plus-circle me-2"></i>添加新版本
</button>
</div>
</div>
<?php if (!empty($success)): ?>
<div class="alert alert-success"><?php echo $success; ?></div>
<?php endif; ?>
<?php if (!empty($error)): ?>
<div class="alert alert-danger"><?php echo $error; ?></div>
<?php endif; ?>
<?php if (empty($versions)): ?>
<div class="alert alert-info">
暂无版本记录
</div>
<?php else: ?>
<div class="row">
<?php foreach ($versions as $version): ?>
<div class="col-md-6 col-lg-4 mb-4">
<div class="card version-card h-100">
<div class="card-body">
<h5 class="card-title"><i class="fas fa-tag me-2"></i>版本 <?php echo htmlspecialchars($version['version']); ?></h5>
<h6 class="card-subtitle mb-2 text-muted">发布日期: <?php echo date('Y-m-d H:i', strtotime($version['created_at'])); ?></h6>
<p class="card-text"><?php echo nl2br(htmlspecialchars($version['changelog'])); ?></p>
</div>
<div class="card-footer bg-transparent d-flex justify-content-between align-items-center">
<small class="text-muted">文件大小: <?php
$filePath = $version['file_path'];
if (file_exists($filePath)) {
echo filesize($filePath) > 0 ? number_format(filesize($filePath) / 1024 / 1024, 2) . ' MB' : '未知';
} else {
echo '文件不存在';
}
?></small>
<div> <button type="button" class="btn btn-sm btn-outline-secondary action-btn" data-bs-toggle="modal" data-bs-target="#editVersionModal_<?php echo $version['id']; ?>">
<i class="fas fa-edit me-1"></i>编辑
</button>
<a href="../<?php echo htmlspecialchars($version['file_path']); ?>" class="btn btn-sm btn-primary action-btn" download>
<i class="fas fa-download me-1"></i>下载
</a>
<a href="?app_id=<?php echo $appId; ?>&delete_id=<?php echo $version['id']; ?>" class="btn btn-sm btn-outline-danger action-btn" onclick="return confirm('确定要删除此版本吗?');">
<i class="fas fa-trash-alt me-1"></i>删除
</a>
</div>
</div>
</div>
<!-- 编辑版本模态框 -->
<div class="modal fade" id="editVersionModal_<?php echo $version['id']; ?>" tabindex="-1" aria-labelledby="editVersionModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editVersionModalLabel"><i class="fas fa-edit me-2"></i>编辑版本 <?php echo htmlspecialchars($version['version']); ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form method="post" enctype="multipart/form-data">
<div class="modal-body">
<input type="hidden" name="version_id" value="<?php echo $version['id']; ?>">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="version_<?php echo $version['id']; ?>" name="version" value="<?php echo htmlspecialchars($version['version']); ?>" placeholder="如: 1.0.0" required>
<label for="version_<?php echo $version['id']; ?>"><i class="fas fa-hashtag me-2"></i>版本号</label>
</div>
<div class="form-floating mb-3">
<textarea class="form-control" id="changelog_<?php echo $version['id']; ?>" name="changelog" rows="3" placeholder="描述本次更新内容" required><?php echo htmlspecialchars($version['changelog']); ?></textarea>
<label for="changelog_<?php echo $version['id']; ?>"><i class="fas fa-history me-2"></i>更新日志</label>
</div>
<div class="mb-3">
<label for="new_app_file_<?php echo $version['id']; ?>" class="form-label"><i class="fas fa-upload me-2"></i>更新App文件 (可选)</label>
<input class="form-control" type="file" id="new_app_file_<?php echo $version['id']; ?>" name="new_app_file">
<div class="form-text">当前文件: <?php echo basename($version['file_path']); ?></div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="fas fa-times-circle me-1"></i>取消
</button>
<button type="submit" class="btn btn-primary" name="edit_version">
<i class="fas fa-save me-1"></i>保存更改
</button>
</div>
</form>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<!-- 添加版本模态框 -->
<div class="modal fade" id="addVersionModal" tabindex="-1" aria-labelledby="addVersionModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addVersionModalLabel"><i class="fas fa-plus-circle me-2"></i>添加新版本</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form method="post" enctype="multipart/form-data">
<div class="modal-body">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="version" name="version" placeholder="如: 1.0.0" required>
<label for="version"><i class="fas fa-hashtag me-2"></i>版本号</label>
</div>
<div class="form-floating mb-3">
<textarea class="form-control" id="changelog" name="changelog" rows="3" placeholder="描述本次更新内容" required></textarea>
<label for="changelog"><i class="fas fa-history me-2"></i>更新日志</label>
</div>
<div class="mb-3">
<label for="app_file" class="form-label"><i class="fas fa-upload me-2"></i>App文件</label>
<input class="form-control" type="file" id="app_file" name="app_file" required>
<a href="<?php echo htmlspecialchars($version['file_path']); ?>" class="btn btn-sm btn-primary" download>下载</a>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="submit" class="btn btn-primary" name="add_version">添加版本</button>
</div>
</form>
</div>
</div>
</div>
<!-- Bootstrap JS Bundle with Popper -->
<script src="/js/bootstrap.bundle.js"></script>
<script>
// 导航栏滚动效果
window.addEventListener('scroll', function() {
const navbar = document.querySelector('.navbar');
if (window.scrollY > 10) {
navbar.classList.add('scrolled');
} else {
navbar.classList.remove('scrolled');
}
});
</script>
</body>
</html>