├── 1.png ├── 2.png ├── 3.png ├── LICENSE ├── README.md ├── admin ├── audits.php ├── categories.php ├── index.php ├── logs.php ├── models.php ├── softwares.php └── utils.php ├── ajax_softwares.php ├── assets └── css │ └── custom.css ├── db.php ├── down.sql ├── download.php ├── index.php ├── login.php ├── logout.php └── register.php /1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xixihucxixi/SoftShareweb/34db1cd86a2dff1dee2c61113a2f9c3e0687a881/1.png -------------------------------------------------------------------------------- /2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xixihucxixi/SoftShareweb/34db1cd86a2dff1dee2c61113a2f9c3e0687a881/2.png -------------------------------------------------------------------------------- /3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xixihucxixi/SoftShareweb/34db1cd86a2dff1dee2c61113a2f9c3e0687a881/3.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 xixihucxixi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SoftShareWeb 2 | 3 | 一个简单实用的【PHP+MySQL】开源软件下载与管理平台,具备用户注册/登录、软件下载与分类、软件上传与后台审批、操作日志、AJAX体验美化等功能。适合高校实验/公司内部资料共享、开源会议资源分发及个人学习进阶使用。 4 | ## 项目截图 5 | 6 | 主页演示: 7 | 8 | ![首页](/1.png) 9 | ## 特性 10 | 11 | - 用户免登录浏览,注册后可下载软件资源(支持多用户) 12 | - 软件支持分类、型号管理,支持关键字全局搜索及AJAX无刷新切换 13 | - 管理员后台:具有软件上传/审核、分类型号管理、上传下载日志、权限多级 14 | - 支持软件包文件上传进度条,体验丝滑 15 | - 所有密码采用安全哈希算法存储 16 | - 界面基于 Bootstrap4,简洁美观,移动端友好 17 | 18 | ## 安装 19 | 20 | 1. **克隆代码** 21 | 22 | ```bash 23 | git clone https://github.com/你的用户名/SoftShareHub.git 24 | cd SoftShareWeb 25 | 26 | 2.导入数据库结构 27 | 使用 phpMyAdmin 或 CLI 导入 docs/database.sql 文件(你需备好建表SQL)。 28 | 29 | 3.配置数据库 30 | 31 | 修改 db.php 文件,填写你的数据库主机、用户名、密码和库名。 32 | 33 | 4.确保 uploads/downloads 目录有可写权限 34 | chmod -R 755 downloads 35 | 首次管理员账号 36 | 37 | 5.默认内置:请注册好之后手动修改is_admin为1就是管理员了 38 | 主要目录结构 39 | assets/ # 前端CSS/JS及图片 40 | admin/ # 后台管理代码 41 | downloads/ # 软件包实际存储目录 42 | index.php # 前台首页/列表 43 | login.php # 登录 44 | register.php # 注册 45 | download.php # 下载逻辑(登录校验) 46 | db.php # 数据库连接 47 | ... 48 | 49 | 常见问题 50 | 上传失败? 51 | 检查 PHP 配置中的 upload_max_filesize 和 post_max_size 52 | 进度条无效? 53 | 用 Chrome/Edge/Firefox 等现代浏览器 54 | 登录失败? 55 | 检查数据库密码字段是否为 password_hash 值,不是明文 56 | 贡献 & 扩展 57 | 欢迎 PR,任何问题也可直接提 Issue~ 58 | 59 | 许可证 60 | MIT 61 | 62 | 致谢 63 | 本项目为开源练习范例,适当参考、二次开发请注明原作者及项目来源。 64 | 65 | -------------------------------------------------------------------------------- /admin/audits.php: -------------------------------------------------------------------------------- 1 | prepare("UPDATE softwares SET is_audited=1 WHERE id=?")->execute([$id]); 9 | header('Location: audits.php'); 10 | exit(); 11 | } 12 | // 驳回(删除) 13 | if (isset($_GET['reject'])) { 14 | $id = intval($_GET['reject']); 15 | $stmt = $pdo->prepare("SELECT filename FROM softwares WHERE id=?"); 16 | $stmt->execute([$id]); 17 | $sw = $stmt->fetch(); 18 | if ($sw) @unlink("../downloads/".$sw['filename']); 19 | $pdo->prepare("DELETE FROM softwares WHERE id=?")->execute([$id]); 20 | header('Location: audits.php'); 21 | exit(); 22 | } 23 | // 查询未审核 24 | $list = $pdo->query("SELECT s.*, c.name categ, m.name model 25 | FROM softwares s 26 | LEFT JOIN categories c ON s.category_id=c.id 27 | LEFT JOIN models m ON s.model_id=m.id 28 | WHERE s.is_audited=0 ORDER BY s.uploaded_at DESC")->fetchAll(); 29 | ?> 30 | 31 | 32 | 33 | 34 | 软件审核 35 | 36 | 37 | 38 | 39 |
40 | ⬅ 返回后台 41 |

待审核软件

42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 55 | 56 | 57 |
名称分类型号描述上传时间操作
52 | 通过 53 | 驳回 54 |
58 |
59 | 60 | -------------------------------------------------------------------------------- /admin/categories.php: -------------------------------------------------------------------------------- 1 | prepare("INSERT IGNORE INTO categories (name) VALUES (?)"); 10 | $stmt->execute([$name]); 11 | } 12 | header('Location: categories.php'); 13 | exit(); 14 | } 15 | // 删除 16 | if (isset($_GET['delete'])) { 17 | $id = intval($_GET['delete']); 18 | $pdo->prepare("DELETE FROM categories WHERE id=?")->execute([$id]); 19 | header('Location: categories.php'); 20 | exit(); 21 | } 22 | $list = $pdo->query("SELECT * FROM categories ORDER BY id")->fetchAll(); 23 | ?> 24 | 25 | 26 | 27 | 分类管理 28 | 29 | 30 | 31 | 32 |
33 | ⬅ 返回后台 34 |

分类管理

35 |
36 | 37 | 38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
ID名称操作
删除
49 |
50 | 51 | -------------------------------------------------------------------------------- /admin/index.php: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 后台管理中心 13 | 14 | 15 | 16 | 17 | 22 |
23 |

管理功能区

24 | 31 |
32 | 33 | 34 | -------------------------------------------------------------------------------- /admin/logs.php: -------------------------------------------------------------------------------- 1 | query("SELECT l.*, u.username, s.name soft 5 | FROM upload_logs l LEFT JOIN users u ON l.user_id=u.id LEFT JOIN softwares s ON l.software_id=s.id 6 | ORDER BY l.upload_time DESC LIMIT 200")->fetchAll(); 7 | $downlog = $pdo->query("SELECT l.*, u.username, s.name soft 8 | FROM download_logs l LEFT JOIN users u ON l.user_id=u.id LEFT JOIN softwares s ON l.software_id=s.id 9 | ORDER BY l.download_time DESC LIMIT 200")->fetchAll(); 10 | ?> 11 | 12 | 13 | 14 | 15 | 上传/下载日志 16 | 17 | 18 | 19 | 20 |
21 | ⬅ 返回后台 22 |

最近上传日志

23 | 24 | 25 | 26 | 27 | 28 |
用户软件上传时间
29 |

最近下载日志

30 | 31 | 32 | 33 | 34 | 35 |
用户软件下载时间
36 |
37 | 38 | -------------------------------------------------------------------------------- /admin/models.php: -------------------------------------------------------------------------------- 1 | query("SELECT * FROM categories ORDER BY id")->fetchAll(); 5 | // 新增 6 | if ($_SERVER['REQUEST_METHOD']=='POST' && isset($_POST['name']) && isset($_POST['category_id'])) { 7 | $name=trim($_POST['name']); 8 | $cid=intval($_POST['category_id']); 9 | if ($name && $cid) { 10 | $stmt = $pdo->prepare("INSERT INTO models (category_id, name) VALUES (?,?)"); 11 | $stmt->execute([$cid, $name]); 12 | } 13 | header('Location: models.php'); 14 | exit(); 15 | } 16 | // 删除 17 | if (isset($_GET['delete'])) { 18 | $id = intval($_GET['delete']); 19 | $pdo->prepare("DELETE FROM models WHERE id=?")->execute([$id]); 20 | header('Location: models.php'); 21 | exit(); 22 | } 23 | $list = $pdo->query("SELECT m.*, c.name cname FROM models m LEFT JOIN categories c ON m.category_id=c.id ORDER BY m.category_id, m.id")->fetchAll(); 24 | ?> 25 | 26 | 27 | 28 | 型号管理 29 | 30 | 31 | 32 | 33 |
34 | ⬅ 返回后台 35 |

型号管理

36 |
37 | 40 | 41 | 42 |
43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 |
ID所属分类型号名称操作
删除
54 |
55 | 56 | -------------------------------------------------------------------------------- /admin/softwares.php: -------------------------------------------------------------------------------- 1 | query("SELECT * FROM categories ORDER BY id")->fetchAll(); 10 | $mdl_map = []; 11 | foreach ($cats as $cat) { 12 | $mdl_map[$cat['id']] = $pdo->query("SELECT * FROM models WHERE category_id={$cat['id']}")->fetchAll(); 13 | } 14 | 15 | // 上传功能 16 | if ($_SERVER['REQUEST_METHOD']=='POST' && isset($_POST['op']) && $_POST['op']=='add') { 17 | $name = trim($_POST['name']); 18 | $description = trim($_POST['description']); 19 | $category_id = intval($_POST['category_id']); 20 | $model_id = intval($_POST['model_id']); 21 | $file = $_FILES['file']; 22 | if ($file['error'] > 0 || !$file['name']) $err = "请选择上传文件"; 23 | else { 24 | $tmp = $file['tmp_name']; 25 | $filename = date('YmdHis').'_'.uniqid().strrchr($file['name'], '.'); 26 | $hash = hash_file('sha256', $tmp); 27 | $cstmt = $pdo->prepare("SELECT id FROM softwares WHERE category_id=? AND model_id=? AND (filehash=?)"); 28 | $cstmt->execute([$category_id, $model_id, $hash]); 29 | if ($cstmt->fetch()) $err = "该文件已存在"; 30 | else { 31 | move_uploaded_file($tmp, "../downloads/$filename"); 32 | $stmt = $pdo->prepare("INSERT INTO softwares (category_id, model_id, name, description, filename, filehash, uploader_id, is_audited) VALUES (?,?,?,?,?,?,?,1)"); 33 | $stmt->execute([$category_id, $model_id, $name, $description, $filename, $hash, $_SESSION['user_id']]); 34 | $succ = "上传成功"; 35 | } 36 | } 37 | // 如果是AJAX上传,直接简明输出 38 | if (isset($_SERVER['HTTP_X_REQUESTED_WITH'])) { 39 | if (!empty($succ)) echo ''.$succ.''; 40 | else echo ''.$err.''; 41 | exit; 42 | } 43 | } 44 | 45 | // 删除功能 46 | if (isset($_GET['delete'])) { 47 | $id = intval($_GET['delete']); 48 | $stmt = $pdo->prepare("SELECT filename FROM softwares WHERE id=?"); 49 | $stmt->execute([$id]); 50 | $sw = $stmt->fetch(); 51 | if ($sw) @unlink("../downloads/".$sw['filename']); 52 | $stmt = $pdo->prepare("DELETE FROM softwares WHERE id=?"); 53 | $stmt->execute([$id]); 54 | header('Location: softwares.php'); 55 | exit(); 56 | } 57 | 58 | // 编辑功能 59 | if ($_SERVER['REQUEST_METHOD']=='POST' && isset($_POST['op']) && $_POST['op']=='edit') { 60 | $id = intval($_POST['id']); 61 | $name = trim($_POST['name']); 62 | $description = trim($_POST['description']); 63 | $category_id = intval($_POST['category_id']); 64 | $model_id = intval($_POST['model_id']); 65 | $stmt = $pdo->prepare("UPDATE softwares SET name=?,description=?, category_id=?, model_id=? WHERE id=?"); 66 | $stmt->execute([$name, $description, $category_id, $model_id, $id]); 67 | header("Location: softwares.php"); 68 | exit(); 69 | } 70 | 71 | // 列表展示 72 | $list = $pdo->query("SELECT s.*, c.name categ, m.name model 73 | FROM softwares s 74 | LEFT JOIN categories c ON s.category_id=c.id 75 | LEFT JOIN models m ON s.model_id=m.id 76 | ORDER BY s.uploaded_at DESC")->fetchAll(); 77 | 78 | ?> 79 | 80 | 81 | 82 | 83 | 软件管理与上传 84 | 85 | 86 | 91 | 92 | 93 |
94 | 97 |

软件管理 98 | 99 |

100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 119 | 120 | 121 | 122 |
ID名称分类型号描述上传时间操作
116 | 编辑 117 | 删除 118 |
123 | 124 | 125 |
126 |
127 | 上传软件 128 | 129 |
130 |
131 |
132 | 133 |
134 | 135 |
136 |
137 | 138 |
139 |
140 | 141 | 145 |
146 |
147 | 148 | 149 |
150 |
151 | 152 | 153 |
154 |
0%
155 |
156 |
157 | 158 | 159 |
160 |
161 |
162 |
163 | 164 | 165 | query("SELECT * FROM softwares WHERE id=$eid")->fetch(); 168 | ?> 169 | 202 | 203 |
204 | 205 | 260 | 261 | -------------------------------------------------------------------------------- /admin/utils.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT * FROM models WHERE category_id=? ORDER BY id"); 5 | $stmt->execute([$cat_id]); 6 | return $stmt->fetchAll(); 7 | } 8 | 9 | // 获取所有分类 10 | function get_all_categories($pdo) { 11 | return $pdo->query("SELECT * FROM categories")->fetchAll(); 12 | } 13 | 14 | // 安全输出 15 | function h($str) { 16 | return htmlspecialchars($str, ENT_QUOTES); 17 | } 18 | ?> -------------------------------------------------------------------------------- /ajax_softwares.php: -------------------------------------------------------------------------------- 1 | prepare( 10 | "SELECT s.*, m.name modelname FROM softwares s 11 | LEFT JOIN models m ON s.model_id=m.id 12 | WHERE (s.name LIKE ? OR s.description LIKE ?) AND s.is_audited=1 13 | ORDER BY s.uploaded_at DESC" 14 | ); 15 | $stmt->execute(['%'.$kw.'%', '%'.$kw.'%']); 16 | } else { 17 | $cid = intval($_GET['catid']??0); 18 | $stmt = $pdo->prepare( 19 | "SELECT s.*, m.name modelname FROM softwares s 20 | LEFT JOIN models m ON s.model_id=m.id 21 | WHERE s.category_id=? AND s.is_audited=1 22 | ORDER BY s.uploaded_at DESC" 23 | ); 24 | $stmt->execute([$cid]); 25 | } 26 | $softs = $stmt->fetchAll(PDO::FETCH_ASSOC); 27 | ?> 28 | 29 |
暂无匹配的软件数据。
30 | 31 |
32 | 33 |
34 |
35 |
36 |
37 |
38 | 型号:  更新时间: 39 |
40 |

41 | 42 | 下载 43 | 44 | 登录后下载 45 | 46 |
47 |
48 |
49 | 50 |
51 | -------------------------------------------------------------------------------- /assets/css/custom.css: -------------------------------------------------------------------------------- 1 | /* 通用背景和字体微调 */ 2 | body { 3 | background-color: #f8f9fa; 4 | font-family: "PingFang SC", "Microsoft YaHei", Arial, sans-serif; 5 | color: #222; 6 | } 7 | 8 | /* 顶部导航栏LOGO与标题 */ 9 | .navbar-brand { 10 | font-weight: bold; 11 | font-size: 22px; 12 | letter-spacing: 2px; 13 | } 14 | 15 | /* 左侧分类栏 */ 16 | .sidebar { 17 | background: #fff; 18 | border-radius: 5px; 19 | box-shadow: 0 1px 8px rgba(0,0,0,0.04); 20 | } 21 | .list-group-item.category-active { 22 | background: #007bff !important; 23 | color: #fff; 24 | font-weight: bold; 25 | border-color: #003d80; 26 | } 27 | 28 | /* 卡片美化 */ 29 | .card { 30 | border-radius: 4px; 31 | box-shadow: 0 2px 12px rgba(0,0,0,0.07); 32 | } 33 | .card-title { 34 | font-size: 18px; 35 | font-weight: bold; 36 | } 37 | 38 | /* 上传弹窗 */ 39 | #addform { 40 | box-shadow: 0 4px 18px rgba(0,0,0,0.15); 41 | border-radius: 7px; 42 | } 43 | 44 | /* 编辑弹窗覆盖层 */ 45 | .modal { 46 | z-index: 9999; 47 | } 48 | 49 | /* 上传进度条(Bootstrap风格) */ 50 | .progress { 51 | height: 25px; 52 | border-radius: 6px; 53 | background: #e9ecef; 54 | } 55 | .progress-bar { 56 | font-weight: bold; 57 | background-color: #4caf50 !important; 58 | color: #fff; 59 | transition: width 0.3s; 60 | } 61 | 62 | /* 上传/下载按钮特效 */ 63 | .download-btn, .btn-success { 64 | color: #fff !important; 65 | background: #28a745; 66 | border: none; 67 | padding: 4px 16px; 68 | border-radius: 3px; 69 | font-size: 15px; 70 | } 71 | .download-btn:hover, .btn-success:hover { 72 | background: #218838 !important; 73 | } 74 | 75 | /* 错误/警告提示 */ 76 | .alert { 77 | font-size: 0.97rem; 78 | border-radius: 5px; 79 | } 80 | .alert-danger { 81 | background: #ffe5e8; 82 | border-color: #f5c2c7; 83 | color: #fa2848; 84 | } 85 | .alert-success { 86 | background: #ebfaeb; 87 | border-color: #a2dca2; 88 | color: #258825; 89 | } 90 | .alert-warning { 91 | background: #fff6e3; 92 | border-color: #ffe885; 93 | color: #946200; 94 | } 95 | 96 | /* form居中小巧卡片 */ 97 | .login-box, .register-box { 98 | max-width: 380px; 99 | margin: 60px auto; 100 | } 101 | .card-header { 102 | font-size: 20px; 103 | } 104 | .form-control:focus { 105 | border-color: #0099ff; 106 | box-shadow: 0 0 4px #9fd9ff; 107 | } 108 | 109 | /* 表格美化 */ 110 | .table th { 111 | background: #e9ecef; 112 | } 113 | 114 | @media (max-width: 768px) { 115 | .sidebar { min-width:inherit; } 116 | .card { box-shadow: none;} 117 | #addform { left:10px; right:10px; margin-left:0; width:auto;} 118 | } 119 | -------------------------------------------------------------------------------- /db.php: -------------------------------------------------------------------------------- 1 | PDO::ERRMODE_EXCEPTION, 11 | PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, 12 | PDO::ATTR_EMULATE_PREPARES => false, 13 | ]; 14 | try { 15 | $pdo = new PDO($dsn, $db_user, $db_pass, $options); 16 | } catch (PDOException $e) { 17 | exit('数据库连接失败: ' . $e->getMessage()); 18 | } 19 | 20 | // 启动 session 21 | if (session_status() == PHP_SESSION_NONE) { 22 | session_start(); 23 | } 24 | 25 | // 如果有登录用户,便捷补充用户名 session 字段 26 | if (!empty($_SESSION['user_id']) && empty($_SESSION['username'])) { 27 | // 下方代码可选,用于在登录后刷新页面时 $_SESSION['username'] 始终存在 28 | $stmt = $pdo->prepare('SELECT username FROM users WHERE id=?'); 29 | $stmt->execute([$_SESSION['user_id']]); 30 | if ($row = $stmt->fetch()) { 31 | $_SESSION['username'] = $row['username']; 32 | } 33 | } 34 | ?> 35 | -------------------------------------------------------------------------------- /down.sql: -------------------------------------------------------------------------------- 1 | -- phpMyAdmin SQL Dump 2 | -- version 5.1.1 3 | -- https://www.phpmyadmin.net/ 4 | -- 5 | -- 主机: localhost 6 | -- 生成日期: 2025-04-29 20:14:48 7 | -- 服务器版本: 5.7.43-log 8 | -- PHP 版本: 8.0.26 9 | 10 | SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; 11 | START TRANSACTION; 12 | SET time_zone = "+00:00"; 13 | 14 | 15 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 16 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 17 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 18 | /*!40101 SET NAMES utf8mb4 */; 19 | 20 | -- 21 | -- 数据库: `down` 22 | -- 23 | 24 | -- -------------------------------------------------------- 25 | 26 | -- 27 | -- 表的结构 `admin_logs` 28 | -- 29 | 30 | CREATE TABLE `admin_logs` ( 31 | `id` int(10) UNSIGNED NOT NULL, 32 | `admin_id` int(10) UNSIGNED DEFAULT NULL, 33 | `action` varchar(255) NOT NULL, 34 | `log_time` datetime DEFAULT CURRENT_TIMESTAMP 35 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 36 | 37 | -- -------------------------------------------------------- 38 | 39 | -- 40 | -- 表的结构 `categories` 41 | -- 42 | 43 | CREATE TABLE `categories` ( 44 | `id` int(10) UNSIGNED NOT NULL, 45 | `name` varchar(100) NOT NULL 46 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 47 | 48 | -- -------------------------------------------------------- 49 | 50 | -- 51 | -- 表的结构 `download_logs` 52 | -- 53 | 54 | CREATE TABLE `download_logs` ( 55 | `id` int(10) UNSIGNED NOT NULL, 56 | `user_id` int(10) UNSIGNED NOT NULL, 57 | `software_id` int(10) UNSIGNED NOT NULL, 58 | `download_time` datetime DEFAULT CURRENT_TIMESTAMP 59 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 60 | 61 | -- -------------------------------------------------------- 62 | 63 | -- 64 | -- 表的结构 `models` 65 | -- 66 | 67 | CREATE TABLE `models` ( 68 | `id` int(10) UNSIGNED NOT NULL, 69 | `category_id` int(10) UNSIGNED NOT NULL, 70 | `name` varchar(100) NOT NULL 71 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 72 | 73 | -- -------------------------------------------------------- 74 | 75 | -- 76 | -- 表的结构 `softwares` 77 | -- 78 | 79 | CREATE TABLE `softwares` ( 80 | `id` int(10) UNSIGNED NOT NULL, 81 | `category_id` int(10) UNSIGNED NOT NULL, 82 | `model_id` int(10) UNSIGNED NOT NULL, 83 | `name` varchar(100) NOT NULL, 84 | `description` text, 85 | `filename` varchar(255) NOT NULL, 86 | `filehash` varchar(64) DEFAULT NULL, 87 | `uploader_id` int(10) UNSIGNED NOT NULL, 88 | `uploaded_at` datetime DEFAULT CURRENT_TIMESTAMP, 89 | `is_audited` tinyint(1) DEFAULT '0' 90 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 91 | 92 | -- -------------------------------------------------------- 93 | 94 | -- 95 | -- 表的结构 `upload_logs` 96 | -- 97 | 98 | CREATE TABLE `upload_logs` ( 99 | `id` int(10) UNSIGNED NOT NULL, 100 | `user_id` int(10) UNSIGNED NOT NULL, 101 | `software_id` int(10) UNSIGNED NOT NULL, 102 | `upload_time` datetime DEFAULT CURRENT_TIMESTAMP 103 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 104 | 105 | -- -------------------------------------------------------- 106 | 107 | -- 108 | -- 表的结构 `users` 109 | -- 110 | 111 | CREATE TABLE `users` ( 112 | `id` int(10) UNSIGNED NOT NULL, 113 | `username` varchar(100) NOT NULL, 114 | `password` varchar(255) NOT NULL, 115 | `is_admin` tinyint(1) DEFAULT '0', 116 | `created_at` datetime DEFAULT CURRENT_TIMESTAMP 117 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 118 | 119 | -- 120 | -- 转储表的索引 121 | -- 122 | 123 | -- 124 | -- 表的索引 `admin_logs` 125 | -- 126 | ALTER TABLE `admin_logs` 127 | ADD PRIMARY KEY (`id`), 128 | ADD KEY `admin_id` (`admin_id`); 129 | 130 | -- 131 | -- 表的索引 `categories` 132 | -- 133 | ALTER TABLE `categories` 134 | ADD PRIMARY KEY (`id`), 135 | ADD UNIQUE KEY `name` (`name`); 136 | 137 | -- 138 | -- 表的索引 `download_logs` 139 | -- 140 | ALTER TABLE `download_logs` 141 | ADD PRIMARY KEY (`id`), 142 | ADD KEY `user_id` (`user_id`), 143 | ADD KEY `software_id` (`software_id`); 144 | 145 | -- 146 | -- 表的索引 `models` 147 | -- 148 | ALTER TABLE `models` 149 | ADD PRIMARY KEY (`id`), 150 | ADD KEY `category_id` (`category_id`); 151 | 152 | -- 153 | -- 表的索引 `softwares` 154 | -- 155 | ALTER TABLE `softwares` 156 | ADD PRIMARY KEY (`id`), 157 | ADD KEY `category_id` (`category_id`), 158 | ADD KEY `model_id` (`model_id`), 159 | ADD KEY `uploader_id` (`uploader_id`); 160 | 161 | -- 162 | -- 表的索引 `upload_logs` 163 | -- 164 | ALTER TABLE `upload_logs` 165 | ADD PRIMARY KEY (`id`), 166 | ADD KEY `user_id` (`user_id`), 167 | ADD KEY `software_id` (`software_id`); 168 | 169 | -- 170 | -- 表的索引 `users` 171 | -- 172 | ALTER TABLE `users` 173 | ADD PRIMARY KEY (`id`), 174 | ADD UNIQUE KEY `username` (`username`); 175 | 176 | -- 177 | -- 在导出的表使用AUTO_INCREMENT 178 | -- 179 | 180 | -- 181 | -- 使用表AUTO_INCREMENT `admin_logs` 182 | -- 183 | ALTER TABLE `admin_logs` 184 | MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT; 185 | 186 | -- 187 | -- 使用表AUTO_INCREMENT `categories` 188 | -- 189 | ALTER TABLE `categories` 190 | MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT; 191 | 192 | -- 193 | -- 使用表AUTO_INCREMENT `download_logs` 194 | -- 195 | ALTER TABLE `download_logs` 196 | MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT; 197 | 198 | -- 199 | -- 使用表AUTO_INCREMENT `models` 200 | -- 201 | ALTER TABLE `models` 202 | MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT; 203 | 204 | -- 205 | -- 使用表AUTO_INCREMENT `softwares` 206 | -- 207 | ALTER TABLE `softwares` 208 | MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT; 209 | 210 | -- 211 | -- 使用表AUTO_INCREMENT `upload_logs` 212 | -- 213 | ALTER TABLE `upload_logs` 214 | MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT; 215 | 216 | -- 217 | -- 使用表AUTO_INCREMENT `users` 218 | -- 219 | ALTER TABLE `users` 220 | MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT; 221 | 222 | -- 223 | -- 限制导出的表 224 | -- 225 | 226 | -- 227 | -- 限制表 `admin_logs` 228 | -- 229 | ALTER TABLE `admin_logs` 230 | ADD CONSTRAINT `admin_logs_ibfk_1` FOREIGN KEY (`admin_id`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE; 231 | 232 | -- 233 | -- 限制表 `download_logs` 234 | -- 235 | ALTER TABLE `download_logs` 236 | ADD CONSTRAINT `download_logs_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, 237 | ADD CONSTRAINT `download_logs_ibfk_2` FOREIGN KEY (`software_id`) REFERENCES `softwares` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; 238 | 239 | -- 240 | -- 限制表 `models` 241 | -- 242 | ALTER TABLE `models` 243 | ADD CONSTRAINT `models_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; 244 | 245 | -- 246 | -- 限制表 `softwares` 247 | -- 248 | ALTER TABLE `softwares` 249 | ADD CONSTRAINT `softwares_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, 250 | ADD CONSTRAINT `softwares_ibfk_2` FOREIGN KEY (`model_id`) REFERENCES `models` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, 251 | ADD CONSTRAINT `softwares_ibfk_3` FOREIGN KEY (`uploader_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; 252 | 253 | -- 254 | -- 限制表 `upload_logs` 255 | -- 256 | ALTER TABLE `upload_logs` 257 | ADD CONSTRAINT `upload_logs_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, 258 | ADD CONSTRAINT `upload_logs_ibfk_2` FOREIGN KEY (`software_id`) REFERENCES `softwares` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; 259 | COMMIT; 260 | 261 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 262 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 263 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 264 | -------------------------------------------------------------------------------- /download.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT * FROM softwares WHERE id=? AND is_audited=1"); 17 | $stmt->execute([$id]); 18 | $sw = $stmt->fetch(); 19 | if (!$sw) { 20 | die('该软件不存在或未通过审核!'); 21 | } 22 | 23 | $filepath = __DIR__ . '/downloads/' . $sw['filename']; 24 | if (!is_file($filepath)) { 25 | die('文件已被删除或找不到!'); 26 | } 27 | 28 | // 记录日志 29 | $stmt = $pdo->prepare("INSERT INTO download_logs (user_id, software_id) VALUES (?, ?)"); 30 | $stmt->execute([$_SESSION['user_id'], $id]); 31 | 32 | // 输出下载 33 | header('Content-Description: File Transfer'); 34 | header('Content-Type: application/octet-stream'); 35 | header('Content-Disposition: attachment; filename="' . rawurlencode($sw['filename']) . '"'); 36 | header('Content-Length: ' . filesize($filepath)); 37 | header('Expires: 0'); 38 | header('Cache-Control: must-revalidate'); 39 | header('Pragma: public'); 40 | readfile($filepath); 41 | exit; -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | query("SELECT * FROM categories ORDER BY id")->fetchAll(PDO::FETCH_ASSOC); 5 | $first_cid = $categories ? $categories[0]['id'] : 0; 6 | $login = !empty($_SESSION['user_id']); 7 | ?> 8 | 9 | 10 | 11 | 12 | 软件下载中心 13 | 14 | 15 | 18 | 19 | 20 | 32 |
33 |
34 | 35 | 46 | 47 |
48 | 49 |
50 | 51 | 52 | 53 |
54 |

软件下载列表

55 |
56 |
加载中...
57 |
58 |
59 |
60 |
61 | 62 | 114 | 115 | -------------------------------------------------------------------------------- /login.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT * FROM users WHERE username = ?"); 13 | $stmt->execute([$username]); 14 | $user = $stmt->fetch(); 15 | if ($user && password_verify($password, $user['password'])) { 16 | // 写入Session 17 | $_SESSION['user_id'] = $user['id']; 18 | $_SESSION['is_admin'] = $user['is_admin']; 19 | $_SESSION['username'] = $user['username']; 20 | header('Location: index.php'); 21 | exit(); 22 | } else { 23 | $error = '用户名或密码错误!'; 24 | } 25 | } 26 | } 27 | ?> 28 | 29 | 30 | 31 | 32 | 用户登录 33 | 34 | 38 | 39 | 40 |
41 |
42 |
43 |

用户登录

44 |
45 |
46 | 47 |
48 | 49 |
50 |
51 | 52 | 53 |
54 |
55 | 56 | 57 |
58 | 59 |
60 |
61 |

没有账号?用户注册

62 |
63 |
64 |
65 | 66 | -------------------------------------------------------------------------------- /logout.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /register.php: -------------------------------------------------------------------------------- 1 | 20) { 14 | $error = '用户名长度需在3到20字符之间。'; 15 | } elseif ($password !== $password2) { 16 | $error = '两次输入的密码不一致!'; 17 | } elseif (strlen($password) < 6) { 18 | $error = '密码长度不能少于6位。'; 19 | } else { 20 | // 检查用户是否存在 21 | $stmt = $pdo->prepare("SELECT id FROM users WHERE username=?"); 22 | $stmt->execute([$username]); 23 | if ($stmt->fetch()) { 24 | $error = '该用户名已被注册!'; 25 | } else { 26 | // 写入新用户 27 | $hash = password_hash($password, PASSWORD_DEFAULT); 28 | $stmt = $pdo->prepare("INSERT INTO users (username, password, is_admin) VALUES (?, ?, 0)"); 29 | $stmt->execute([$username, $hash]); 30 | header('Location: login.php'); 31 | exit(); 32 | } 33 | } 34 | } 35 | ?> 36 | 37 | 38 | 39 | 40 | 用户注册 41 | 42 | 46 | 47 | 48 |
49 |
50 |
51 |

用户注册

52 |
53 |
54 | 55 |
56 | 57 |
58 |
59 | 60 | 62 |
63 |
64 | 65 | 67 |
68 |
69 | 70 | 72 |
73 | 74 |
75 |
76 |

已有账号?立即登录

77 |
78 |
79 |
80 | 81 | --------------------------------------------------------------------------------