Files
leonapp/developer/edit_app.php
Leonmmcoset 99eb4d1800 feat(developer): 添加转载警告并改进标签选择方式
在应用上传页面添加非原创应用的转载警告提示
将编辑页面的标签选择从复选框改为多选下拉框,提升用户体验
2025-07-11 18:55:04 +08:00

366 lines
16 KiB
PHP
Raw 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['developer_id'])) {
header('Location: login.php');
exit;
}
$developerId = $_SESSION['developer_id'];
$error = '';
$success = '';
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
header('Location: dashboard.php');
exit;
}
$appId = $_GET['id'];
$app = [];
// 检查数据库连接是否为 MySQLi 对象
if (!($conn instanceof mysqli)) {
log_error('数据库连接错误: 连接不是MySQLi实例', __FILE__, __LINE__);
$error = '数据库连接错误,请检查配置';
header('Location: dashboard.php');
exit;
}
// 获取所有标签
$tags = [];
$tagStmt = $conn->query('SELECT id, name FROM tags');
while ($tag = $tagStmt->fetch_assoc()) {
$tags[] = $tag;
}
$tagStmt->close();
// 获取应用现有标签
$appTags = [];
$appTagStmt = $conn->prepare('SELECT tag_id FROM app_tags WHERE app_id = ?');
$appTagStmt->bind_param('i', $appId);
$appTagStmt->execute();
$appTagResult = $appTagStmt->get_result();
while ($tag = $appTagResult->fetch_assoc()) {
$appTags[] = $tag['tag_id'];
}
$appTagStmt->close();
// 获取应用信息并验证开发者权限
$stmt = $conn->prepare('SELECT id, name, description, version, changelog, age_rating, age_rating_description, platforms, file_path FROM apps WHERE id = ? AND developer_id = ?');
if (!$stmt) {
log_error('获取应用信息查询准备失败: ' . $conn->error, __FILE__, __LINE__);
$error = '获取应用信息时发生错误,请稍后再试';
header('Location: dashboard.php');
exit;
}
$stmt->bind_param('ii', $appId, $developerId);
if (!$stmt->execute()) {
log_error('获取应用信息查询执行失败: ' . $stmt->error, __FILE__, __LINE__);
$error = '获取应用信息时发生错误,请稍后再试';
header('Location: dashboard.php');
exit;
}
$result = $stmt->get_result();
$app = $result->fetch_assoc();
if (!$app) {
header('Location: dashboard.php');
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$appName = trim($_POST['name']);
$appDescription = trim($_POST['description']);
$version = trim($_POST['version']);
$changelog = trim($_POST['changelog']);
$ageRating = $_POST['age_rating'];
$ageRatingDescription = trim($_POST['age_rating_description']);
$platforms = $_POST['platforms'] ?? [];
$platforms_json = json_encode($platforms);
$appFilePath = $app['file_path']; // 默认使用现有文件路径
// 获取选中的标签
$selectedTags = $_POST['tags'] ?? [];
// 处理应用文件上传
if (!empty($_FILES['app_file']['tmp_name'])) {
$uploadDir = '../uploads/apps/';
$fileExtension = pathinfo($_FILES['app_file']['name'], PATHINFO_EXTENSION);
$newFileName = uniqid() . '.' . $fileExtension;
$targetPath = $uploadDir . $newFileName;
// 验证文件类型和大小
$allowedTypes = ['apk', 'exe', 'jar', 'crx', 'ini'];
if (!in_array($fileExtension, $allowedTypes)) {
$error = '不支持的文件类型,请上传 ' . implode(', ', $allowedTypes) . ' 格式的文件';
} elseif ($_FILES['app_file']['size'] > 50 * 1024 * 1024) { // 50MB
$error = '文件大小不能超过50MB';
} elseif (!move_uploaded_file($_FILES['app_file']['tmp_name'], $targetPath)) {
$error = '文件上传失败,请检查服务器权限';
} else {
// 删除旧文件
if (file_exists($appFilePath)) {
unlink($appFilePath);
}
$appFilePath = $targetPath;
}
}
// 处理图片删除
if (!empty($_POST['removed_images'])) {
$removedImageIds = explode(',', $_POST['removed_images']);
foreach ($removedImageIds as $imgId) {
if (is_numeric($imgId)) {
// 获取图片路径
$stmt = $conn->prepare("SELECT image_path FROM app_images WHERE id = ?");
$stmt->bind_param("i", $imgId);
$stmt->execute();
$imgResult = $stmt->get_result();
if ($img = $imgResult->fetch_assoc()) {
// 删除文件
if (file_exists($img['image_path'])) {
unlink($img['image_path']);
}
// 删除数据库记录
$deleteStmt = $conn->prepare("DELETE FROM app_images WHERE id = ?");
$deleteStmt->bind_param("i", $imgId);
$deleteStmt->execute();
$deleteStmt->close();
}
$stmt->close();
}
}
}
// 更新应用标签
// 删除现有标签关联
$deleteTagStmt = $conn->prepare('DELETE FROM app_tags WHERE app_id = ?');
$deleteTagStmt->bind_param('i', $appId);
$deleteTagStmt->execute();
$deleteTagStmt->close();
// 添加新标签关联
foreach ($selectedTags as $tagId) {
if (is_numeric($tagId)) {
$tagStmt = $conn->prepare('INSERT INTO app_tags (app_id, tag_id) VALUES (?, ?)');
$tagStmt->bind_param('ii', $appId, $tagId);
$tagStmt->execute();
$tagStmt->close();
}
}
// 处理新图片上传
$imageUploadDir = '../uploads/images/';
$allowedImageTypes = ['jpg', 'jpeg', 'png'];
$maxImages = 5;
$currentImageCount = count($existingImages) - count($removedImageIds ?? []);
if (!empty($_FILES['images']['name'][0]) && empty($error)) {
$newImages = $_FILES['images'];
for ($i = 0; $i < count($newImages['name']); $i++) {
if ($newImages['error'][$i] !== UPLOAD_ERR_OK) continue;
$fileName = $newImages['name'][$i];
$fileTmp = $newImages['tmp_name'][$i];
$fileSize = $newImages['size'][$i];
$fileExt = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
if (!in_array($fileExt, $allowedImageTypes)) {
$error = "图片 {$fileName} 格式不支持仅允许jpg、png";
break;
}
if ($fileSize > 2 * 1024 * 1024) { // 2MB
$error = "图片 {$fileName} 大小超过2MB";
break;
}
if ($currentImageCount >= $maxImages) {
$error = "最多只能上传5张图片";
break;
}
$newFileName = uniqid() . '.' . $fileExt;
$targetPath = $imageUploadDir . $newFileName;
if (move_uploaded_file($fileTmp, $targetPath)) {
// 插入数据库
$stmt = $conn->prepare("INSERT INTO app_images (app_id, image_path) VALUES (?, ?)");
$stmt->bind_param("is", $appId, $targetPath);
$stmt->execute();
$stmt->close();
$currentImageCount++;
} else {
$error = "图片 {$fileName} 上传失败";
break;
}
}
}
// 验证标签选择
if (empty($selectedTags)) {
$error = '至少需要选择一个应用标签';
}
if (empty($appName) || empty($appDescription) || empty($version) || empty($changelog) || empty($ageRating) || empty($ageRatingDescription)) {
$error = '应用名称和描述不能为空';
} else {
// 检查数据库连接是否为 MySQLi 对象
if (!($conn instanceof mysqli)) {
log_error('数据库连接错误: 连接不是MySQLi实例', __FILE__, __LINE__);
$error = '数据库连接错误,请检查配置';
} else {
$platforms = $_POST['platforms'] ?? [];
$platforms_json = json_encode($platforms);
$stmt = $conn->prepare('UPDATE apps SET name = ?, description = ?, version = ?, changelog = ?, age_rating = ?, age_rating_description = ?, platforms = ?, file_path = ?, status = \'pending\' WHERE id = ? AND developer_id = ?');
if (!$stmt) {
log_error('更新应用信息查询准备失败: ' . $conn->error, __FILE__, __LINE__);
$error = '更新应用信息时发生错误,请稍后再试';
} else {
$stmt->bind_param('ssssssssii', $appName, $appDescription, $version, $changelog, $ageRating, $ageRatingDescription, $platforms_json, $appFilePath, $appId, $developerId);
if (!$stmt->execute()) {
log_error('更新应用信息查询执行失败: ' . $stmt->error, __FILE__, __LINE__);
$error = '更新应用信息时发生错误,请稍后再试';
} else {
$success = '应用信息更新成功,请等待管理员重新审核';
header('Location: dashboard.php?success=' . urlencode($success));
exit;
$app['name'] = $appName;
$app['description'] = $appDescription;
}
}
}
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>编辑应用</title>
<link href="../css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<h2 class="text-center mb-4">编辑应用</h2>
<?php if (!empty($error)): ?>
<div class="alert alert-danger"><?php echo $error; ?></div>
<?php endif; ?>
<?php if (!empty($success)): ?>
<div class="alert alert-success"><?php echo $success; ?></div>
<?php endif; ?>
<form method="post" enctype="multipart/form-data">
<div class="mb-3">
<label for="name" class="form-label">应用名称</label>
<input type="text" class="form-control" id="name" name="name" value="<?php echo htmlspecialchars($app['name']); ?>" required>
</div>
<div class="mb-3">
<label for="description" class="form-label">应用描述</label>
<textarea class="form-control" id="description" name="description" rows="5" required><?php echo htmlspecialchars($app['description']); ?></textarea>
</div>
<div class="mb-3">
<label for="version" class="form-label">版本号</label>
<input type="text" class="form-control" id="version" name="version" value="<?php echo htmlspecialchars($app['version']); ?>" required>
</div>
<div class="mb-3">
<label for="changelog" class="form-label">更新日志</label>
<textarea class="form-control" id="changelog" name="changelog" rows="3" required><?php echo htmlspecialchars($app['changelog']); ?></textarea>
</div>
<div class="mb-3">
<label for="age_rating" class="form-label">年龄分级</label>
<select class="form-select" id="age_rating" name="age_rating" required>
<option value="3+" <?php echo $app['age_rating'] === '3+' ? 'selected' : ''; ?>>3+</option>
<option value="7+" <?php echo $app['age_rating'] === '7+' ? 'selected' : ''; ?>>7+</option>
<option value="12+" <?php echo $app['age_rating'] === '12+' ? 'selected' : ''; ?>>12+</option>
<option value="17+" <?php echo $app['age_rating'] === '17+' ? 'selected' : ''; ?>>17+</option>
</select>
</div>
<div class="mb-3">
<label for="age_rating_description" class="form-label">年龄分级说明</label>
<input type="text" class="form-control" id="age_rating_description" name="age_rating_description" value="<?php echo htmlspecialchars($app['age_rating_description']); ?>" required>
</div>
<div class="mb-3">
<label class="form-label">适用平台</label>
<?php $platforms = json_decode($app['platforms'], true) ?? []; ?>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="platform_android" name="platforms[]" value="Android" <?php echo in_array('Android', $platforms) ? 'checked' : ''; ?>>
<label class="form-check-label" for="platform_android">Android</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="platform_ios" name="platforms[]" value="iOS" <?php echo in_array('iOS', $platforms) ? 'checked' : ''; ?>>
<label class="form-check-label" for="platform_ios">iOS</label>
</div>
</div>
<div class="mb-3">
<label for="tags" class="form-label">应用标签 (至少选择1个)</label>
<select id="tags" name="tags[]" multiple class="form-control">
<?php foreach ($tags as $tag): ?>
<option value="<?php echo $tag['id']; ?>" <?php echo in_array($tag['id'], $appTags) ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($tag['name']); ?>
</option>
<?php endforeach; ?>
</select>
<small class="form-text text-muted">按住Ctrl键可选择多个标签</small>
</div>
<div class="mb-3">
<label for="app_file" class="form-label">更新应用文件</label>
<input class="form-control" type="file" id="app_file" name="app_file">
<div class="form-text">当前文件: <?php echo basename($app['file_path']); ?></div>
</div>
<div class="mb-3">
<label class="form-label">应用图片 (最多5张)</label>
<?php
// 获取现有图片
$existingImages = [];
$stmt = $conn->prepare("SELECT id, image_path FROM app_images WHERE app_id = ?");
$stmt->bind_param("i", $appId);
$stmt->execute();
$imgResult = $stmt->get_result();
while ($img = $imgResult->fetch_assoc()) {
$existingImages[] = $img;
}
$stmt->close();
?>
<!-- 现有图片 -->
<?php if (!empty($existingImages)): ?>
<div class="mb-3">
<label>现有图片:</label>
<div class="d-flex flex-wrap gap-2">
<?php foreach ($existingImages as $img): ?>
<div class="position-relative">
<img src="<?php echo htmlspecialchars($img['image_path']); ?>" alt="应用图片" style="width: 100px; height: 100px; object-fit: cover; border-radius: 4px;">
<button type="button" class="btn btn-danger btn-sm position-absolute top-0 end-0" onclick="removeImage(<?php echo $img['id']; ?>)">×</button>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<!-- 新图片上传 -->
<input type="file" name="images[]" multiple accept="image/*" class="form-control">
<small>支持jpg、png格式最多上传5张图片</small>
</div>
<input type="hidden" name="removed_images" id="removed_images" value="">
<button type="submit" class="btn btn-primary w-100">保存更改</button>
</form>
<div class="text-center mt-3">
<a href="dashboard.php" class="btn btn-secondary">返回仪表盘</a>
</div>
</div>
<script>
function removeImage(imageId) {
const removedInput = document.getElementById('removed_images');
const currentValues = removedInput.value ? removedInput.value.split(',') : [];
if (!currentValues.includes(imageId.toString())) {
currentValues.push(imageId);
removedInput.value = currentValues.join(',');
}
// 从DOM中移除图片元素
event.target.closest('.position-relative').remove();
}
</script>
</body>
</html>